103

I'm trying to develop an app that prevents a user from getting to a specified app without a password. The scenario is...

  1. user clicks on "Email" app (for example)
  2. my app detects launch of an app
  3. my app confirms it is the "Email" app
  4. my app opens a view over the top, asking for a password
  5. user enters a password, if correct, my app disappears, leaving the "Email" app on top

I'm ok doing the rest of it, just part 2 is puzzling me, and after many days reading up on Broadcast Intents etc and trying to listen for "android.intent.action.MAIN" etc in my trial projects I can't seem to detect when an app other than mine is started.

Can anyone help? Am I going about it the right way, in looking for new apps broadcasting an intent to start, or should I be reading the system log for new intents, or doing something in native code?

Any pointers would help, even if you can't answer it fully I'll be able to do some more research. Thanks a lot. Ian

teedyay
  • 23,293
  • 19
  • 66
  • 73
Ian
  • 1,243
  • 2
  • 11
  • 14
  • @lan how u resolved your issue can u please share your knowledge – nida Jan 16 '15 at 05:11
  • hi have you got the solution? – ask4solutions Feb 24 '16 at 06:26
  • I'm not sure how they've done it, but apps like [App Protector](http://www.androlib.com/android.application.com-carrotapp-protectdemo12-nFzA.aspx) does exactly what you're asking for, so it is indeed technically possible. – hanspeide Jul 20 '10 at 19:23

8 Answers8

34

I think we can use logcat and analyze it's output.

In all similar programs I have found this permission :

android.permission.READ_LOGS

It means all of them use it but it seems the program starts and after that our program (app protector) will start and bring front.

Use below code :

try
    {
        Process mLogcatProc = null;
        BufferedReader reader = null;
        mLogcatProc = Runtime.getRuntime().exec(new String[]{"logcat", "-d"});

        reader = new BufferedReader(new InputStreamReader(mLogcatProc.getInputStream()));

        String line;
        final StringBuilder log = new StringBuilder();
        String separator = System.getProperty("line.separator"); 

        while ((line = reader.readLine()) != null)
        {
            log.append(line);
            log.append(separator);
        }
        String w = log.toString();
        Toast.makeText(getApplicationContext(),w, Toast.LENGTH_LONG).show();
    }
    catch (Exception e) 
    {
        Toast.makeText(getApplicationContext(), e.getMessage(), Toast.LENGTH_LONG).show();
    }

And do not forget to add it's permission in Manifest file.

gprathour
  • 14,813
  • 5
  • 66
  • 90
M.Movaffagh
  • 1,284
  • 5
  • 20
  • 33
  • please, where do we have to put this code? in a service? in the onStartCommand()? – haythem souissi May 30 '12 at 01:22
  • hello can any one answer this question: http://stackoverflow.com/questions/10874904/detect-when-user-lunch-a-new-application-in-android-device/10875633#10875633 It is related to this topic – haythem souissi Jun 06 '12 at 11:22
  • 62
    will not work from JellyBean and above. READ_LOGS permission is now reserved for system apps only. – Ran Jul 14 '12 at 13:18
  • 6
    Are you absolutely sure about this? Because Smart AppLock seems to be able to do this even on JB devices. Is it because the application elevates itself to the Device Administrator status? https://play.google.com/store/apps/details?id=com.thinkyeah.smartlockfree&feature=nav_other#?t=W251bGwsMSwxLDYsImNvbS50aGlua3llYWguc21hcnRsb2NrZnJlZSJd – Karthik Balakrishnan May 16 '13 at 18:48
  • I have try this code..its work fine but starting thread will gives outofmemory exception because it will going to print lot of logs of emmulator.how to handle this outofmemory exception? – Nitish Patel Jan 15 '14 at 07:14
  • 1
    @Torcellite, that app has the "Get running tasks" permission, so it might be using that technique instead. – Sam Apr 27 '15 at 09:30
  • Smart AppLock is working on my JB (4.3) device reading logs, and magically working on my ICS (4.4) device, which doesn't have ActivityManager logs. – Anubian Noob Jul 05 '15 at 16:06
  • In Android L you should use `android.app.usage` package instead. https://developer.android.com/reference/android/app/usage/package-summary.html – Plo_Koon Sep 06 '15 at 13:36
  • 1
    @Ran so what to do to use it now ...is there a solution available now to solve the problem stated in the question since i need to be above jelly beans...please give your feedback asap... – Shreyan Mehta Apr 24 '17 at 14:46
21

A gimmicky way to do it is have a service with a timed loop that checks

ActivityManager am = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.RunningAppProcessInfo> runningAppProcessInfo = am.getRunningAppProcesses();

You run through that list to look at what is running on the phone. Now you can identify them with ids and processName, so for standard activity this is easy for custom ones well unless you stop them all its hard to discriminate...

Note: this isnt a list of whats is actually on the screen, just a list of whats is running...kinda nullifying your goal maybe but at least you will know when something is starting to run... it will keep being in that list even when in background though.

For the password thing you can just start your activity when you found an app thats protected or whatever.

Aimeric
  • 219
  • 2
  • 2
  • is it possible to obtain the time when the app was launched/resumed? – 0LLiena Jul 17 '14 at 20:44
  • 4
    In Android L use `android.app.usage` package instead. https://developer.android.com/reference/android/app/usage/package-summary.html – Plo_Koon Sep 06 '15 at 13:37
13
class CheckRunningActivity extends Thread{
    ActivityManager am = null;
    Context context = null;

    public CheckRunningActivity(Context con){
        context = con;
        am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
    }

    public void run(){
        Looper.prepare();

        while(true){
            // Return a list of the tasks that are currently running,
            // with the most recent being first and older ones after in order.
            // Taken 1 inside getRunningTasks method means want to take only
            // top activity from stack and forgot the olders.
            List< ActivityManager.RunningTaskInfo > taskInfo = am.getRunningTasks(1);

            String currentRunningActivityName = taskInfo.get(0).topActivity.getClassName();

            if (currentRunningActivityName.equals("PACKAGE_NAME.ACTIVITY_NAME")) {
                // show your activity here on top of PACKAGE_NAME.ACTIVITY_NAME
            }
        }
        Looper.loop();
    }
}

You can get current running Activity and check if this Activity corresponds to Email application.

Run CheckRunningActivity Thread on Application start (or on device boot).

new CheckRunningActivity().start();

Update: This class need android.permission.GET_TASKS permission, so add next line to the Manifest:

<uses-permission android:name="android.permission.GET_TASKS" />
odr_m9611
  • 195
  • 3
  • 16
Veaceslav Gaidarji
  • 4,261
  • 3
  • 38
  • 61
  • I'm using this approach but this will open your "// show your activity here on top of PACKAGE_NAME.ACTIVITY_NAME" again and again because of loop. Any workaround for that? – Anuj Sharma Jul 18 '14 at 09:08
  • stop CheckRunningActivity thread when you get the desired result – Veaceslav Gaidarji Jul 18 '14 at 09:14
  • Thanks for reply, then how/when this thread is restarted again? I am using a Sticky Service. – Anuj Sharma Jul 18 '14 at 09:17
  • depends on context of the problem, describe in more details please what do you want to get. – Veaceslav Gaidarji Jul 18 '14 at 09:21
  • I have a sticky service with the same approach implemented above, now let's say i have stopped the service and passed intent to new Activity (i.e. lock screen), now if the lock code is correct/incorrect i have to start the service again. Now again on same problem it will again move you on the lock screen as the topApplication is in the locked apps list. – Anuj Sharma Jul 18 '14 at 09:25
  • Could you please provide me a working complete example using this approach. – Anuj Sharma Jul 18 '14 at 09:26
  • don't stop your Service. save somewhere in Application (preferences, Application singleton, database), current running activity. if this Activity is lockscreen, simple do nothing, your are already in right place, if no open this "new Activity (i.e. lock screen)". Or when your done with lockscreen (native lockscreen or your custom), send broadcast message to your application, and in BroadcastReceiver start your Service again. – Veaceslav Gaidarji Jul 18 '14 at 09:41
  • Your answer is quiet confusing. I will do search it more for correct answer and solution. Anyhow thanks for your help. – Anuj Sharma Jul 21 '14 at 05:56
  • If somebody cares about the beauty and correctness of the code, then this solution should be used. Definitely better than messing with logcat. – Dumoko Sep 04 '14 at 10:16
  • 3
    In your code here, the `Looper.loop()` statement looks like it will never be executed due to the `while(true)` loop never finishing. Is this a mistake? – Sam Oct 31 '14 at 10:07
  • it's just an idea. when I get required Activity - loop isn't running anymore, break called (inside if). – Veaceslav Gaidarji Oct 31 '14 at 11:41
  • You can't execute this code once or periodically, it's up to developer. – Veaceslav Gaidarji Sep 15 '16 at 07:47
12

I think and hope this is not possible. Consider how easily such functionality could be abused by malicious software. You can listen to intents directed at you, and those that are broadcast, but application launching should not be a broadcast event.

What you may be able to do is replace the launcher. If the user agrees to it.

Pontus Gagge
  • 17,166
  • 1
  • 38
  • 51
  • 1
    Why should it not be possible? It's my device and I decide what to run on it. How is this more of a problem than the other permissions we routinely grant? A replacement launcher will not catch the launch of all apps only those launched directly by it. There are many comments on this and similar threads on SO claiming that being able to simply see intents go by would be some massive problem but no one explains what the problem is and why it should be regarded as so nasty that the existing system of privileges cannot be used to make it clear to the user what is happening. – Kevin Whitefoot Dec 01 '14 at 19:42
  • As prospective permissions go, this one's a doozy. The point of having a security model is to enable most legitimate use cases, while preventing most (ideally all) exploits. It's not just you (presumably a knowledgeable user) who needs to be protected, but also naïve users installing apps, and app writers who are spared from having to consider yet another attack vector. All security is tradeoffs: in this case, between utility and power versus massive exploitability. You are free to clone the Android stack and code your own system if you really want that degree of freedom for yourself. – Pontus Gagge Jan 04 '15 at 19:44
11

The main issue is you are trying to listen for implicit intents when the Launcher (home screen) is typically using explicit intents.

An implicit intent is when you want to say "Somebody play this video" and Android picks an app that can handle that intent.

An explicit intent is what happens when you click the "Email" icon on the home screen. It is specifically telling Android to open that specific app by fully qualified name (i.e. com.android.mail or something).

There is no way AFAIK to intercept such explicit intents. It is a security measure built into Android that no two Activities can have the same fully qualified package name. This prevents a third party from cloning the app and masquerading as that app. If what you wish to do was possible, you could theoretically install an app that could block all of your competition's apps from working.

What you are trying to do goes against the Android security model.

One thing you could do is partner with specific app developers to forward the intents to your security system, but that's probably not something you want to deal with.

CodeFusionMobile
  • 14,812
  • 25
  • 102
  • 140
8

getRunningTasks() is deprecated in Android L.

To obtain app usage statistics you can use UsageStats class from android.app.usage package.

The new App usage statistics API allows app developers to collect statistics related to usage of the applications. This API provides more detailed usage information than the deprecated getRecentTasks() method.

To use this API, you must first declare the android.permission.PACKAGE_USAGE_STATS permission in your manifest. The user must also enable access for this app through Settings > Security > Apps with usage access.

Here is a basic app example showing how to use App usage statistics API to let users collect statistics related to usage of the applications.

Community
  • 1
  • 1
Plo_Koon
  • 2,953
  • 3
  • 34
  • 41
4

Perhaps you need a service, something that will run in the background constantly. Than have your service do what you said. Listen for the android.intent.action.MAIN also with the category android.intent.category.LAUNCHER. Then have that broadcast receiver override the onReceive method and do check to see the name of the application etc.

Jacob Malliet
  • 525
  • 3
  • 11
  • 2
    This sounds like just the method I was thinking about, but i'm struggling to receive the MAIN (cat. LAUNCHER) broadcast with a basic BroadcastReceiver. Has anyone managed to do this before? At this stage i'm just looking to detect that an application has been launched or resumed. I can then compare the package name to a string holding the name(s) i'm looking for. – Ian Jul 21 '10 at 13:47
1

MORE MODERN ANSWER (Nov 2022):

This can be handled by the new app usage API

The system collects the usage data on a per-app basis, aggregating the data over daily, weekly, monthly, and yearly intervals. The maximum duration that the system keeps this data is as follows:

Daily data: 7 days Weekly data: 4 weeks Monthly data: 6 months Yearly data: 2 years For each app, the system records the following data:

The last time the app was used

The total length of time the app was in the foreground for that time interval (by > day, week, month, or year)

Timestamp capturing when a component (identified by a package and activity name) ?> moved to the foreground or background during a day

Timestamp capturing when a device configuration changed (such as when the device orientation changed because of rotation)

A background service can be used to constantly watch for updates to timestamps for specific apps regarding when they moved to the foreground with the API above.

note: It seems that starting from Android 5/6/7 and higher you cannot read info about other apps, including their status and logs. I believe this makes the accepted answer no longer work. If anyone can confirm this please comment below! Also, the getRunningTasks function suggested by @Veaceslav Gaidarji is now deprecated.

JakeD
  • 407
  • 2
  • 7
  • 19