diff options
13 files changed, 234 insertions, 28 deletions
diff --git a/core/java/android/app/usage/UsageEvents.java b/core/java/android/app/usage/UsageEvents.java index ce8b05abf365..0d7a94138411 100644 --- a/core/java/android/app/usage/UsageEvents.java +++ b/core/java/android/app/usage/UsageEvents.java @@ -15,10 +15,13 @@ */ package android.app.usage; +import android.annotation.IntDef; import android.content.res.Configuration; import android.os.Parcel; import android.os.Parcelable; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.util.Arrays; import java.util.List; @@ -28,6 +31,12 @@ import java.util.List; */ public final class UsageEvents implements Parcelable { + /** @hide */ + public static final String INSTANT_APP_PACKAGE_NAME = "android.instant_app"; + + /** @hide */ + public static final String INSTANT_APP_CLASS_NAME = "android.instant_class"; + /** * An event representing a state change for a component. */ @@ -91,6 +100,17 @@ public final class UsageEvents implements Parcelable { */ public static final int CHOOSER_ACTION = 9; + /** @hide */ + public static final int FLAG_IS_PACKAGE_INSTANT_APP = 1 << 0; + + /** @hide */ + @IntDef(flag = true, + value = { + FLAG_IS_PACKAGE_INSTANT_APP, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface EventFlags {} + /** * {@hide} */ @@ -145,6 +165,27 @@ public final class UsageEvents implements Parcelable { */ public String[] mContentAnnotations; + /** @hide */ + @EventFlags + public int mFlags; + + public Event() { + } + + /** @hide */ + public Event(Event orig) { + mPackage = orig.mPackage; + mClass = orig.mClass; + mTimeStamp = orig.mTimeStamp; + mEventType = orig.mEventType; + mConfiguration = orig.mConfiguration; + mShortcutId = orig.mShortcutId; + mAction = orig.mAction; + mContentType = orig.mContentType; + mContentAnnotations = orig.mContentAnnotations; + mFlags = orig.mFlags; + } + /** * The package name of the source of this event. */ @@ -196,6 +237,20 @@ public final class UsageEvents implements Parcelable { public String getShortcutId() { return mShortcutId; } + + /** @hide */ + public Event getObfuscatedIfInstantApp() { + if ((mFlags & FLAG_IS_PACKAGE_INSTANT_APP) == 0) { + return this; + } + final Event ret = new Event(this); + ret.mPackage = INSTANT_APP_PACKAGE_NAME; + ret.mClass = INSTANT_APP_CLASS_NAME; + + // Note there are other string fields too, but they're for app shortcuts and choosers, + // which instant apps can't use anyway, so there's no need to hide them. + return ret; + } } // Only used when creating the resulting events. Not used for reading/unparceling. diff --git a/core/java/android/app/usage/UsageStats.java b/core/java/android/app/usage/UsageStats.java index 0874095d440e..7eef85c44139 100644 --- a/core/java/android/app/usage/UsageStats.java +++ b/core/java/android/app/usage/UsageStats.java @@ -85,6 +85,17 @@ public final class UsageStats implements Parcelable { mChooserCounts = stats.mChooserCounts; } + /** + * {@hide} + */ + public UsageStats getObfuscatedForInstantApp() { + final UsageStats ret = new UsageStats(this); + + ret.mPackageName = UsageEvents.INSTANT_APP_PACKAGE_NAME; + + return ret; + } + public String getPackageName() { return mPackageName; } diff --git a/core/java/android/app/usage/UsageStatsManagerInternal.java b/core/java/android/app/usage/UsageStatsManagerInternal.java index 08595dd1b3b4..dbaace2f0ac9 100644 --- a/core/java/android/app/usage/UsageStatsManagerInternal.java +++ b/core/java/android/app/usage/UsageStatsManagerInternal.java @@ -127,7 +127,12 @@ public abstract class UsageStatsManagerInternal { public abstract void applyRestoredPayload(int user, String key, byte[] payload); - /* Cache Quota Service API */ + /** + * Return usage stats. + * + * @param obfuscateInstantApps whether instant app package names need to be obfuscated in the + * result. + */ public abstract List<UsageStats> queryUsageStatsForUser( - int userId, int interval, long beginTime, long endTime); + int userId, int interval, long beginTime, long endTime, boolean obfuscateInstantApps); } diff --git a/core/java/android/content/pm/PackageManagerInternal.java b/core/java/android/content/pm/PackageManagerInternal.java index 426f3cf792aa..87e6a8465beb 100644 --- a/core/java/android/content/pm/PackageManagerInternal.java +++ b/core/java/android/content/pm/PackageManagerInternal.java @@ -341,4 +341,7 @@ public abstract class PackageManagerInternal { * Return the taget SDK version for the app with the given UID. */ public abstract int getUidTargetSdkVersion(int uid); + + /** Whether the binder caller can access instant apps. */ + public abstract boolean canAccessInstantApps(int callingUid); } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index d171f6eb1ac1..acd9cfad4763 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -3480,6 +3480,31 @@ public class PackageManagerService extends IPackageManager.Stub return cur; } + /** + * Returns whether or not a full application can see an instant application. + * <p> + * Currently, there are three cases in which this can occur: + * <ol> + * <li>The calling application is a "special" process. The special + * processes are {@link Process#SYSTEM_UID}, {@link Process#SHELL_UID} + * and {@code 0}</li> + * <li>The calling application has the permission + * {@link android.Manifest.permission#ACCESS_INSTANT_APPS}</li> + * <li>[TODO] The calling application is the default launcher on the + * system partition.</li> + * </ol> + */ + private boolean canAccessInstantApps(int callingUid) { + final boolean isSpecialProcess = + callingUid == Process.SYSTEM_UID + || callingUid == Process.SHELL_UID + || callingUid == 0; + final boolean allowMatchInstant = + isSpecialProcess + || mContext.checkCallingOrSelfPermission( + android.Manifest.permission.ACCESS_INSTANT_APPS) == PERMISSION_GRANTED; + return allowMatchInstant; + } private PackageInfo generatePackageInfo(PackageSetting ps, int flags, int userId) { if (!sUserManager.exists(userId)) return null; if (ps == null) { @@ -3489,19 +3514,15 @@ public class PackageManagerService extends IPackageManager.Stub if (p == null) { return null; } + final int callingUid = Binder.getCallingUid(); // Filter out ephemeral app metadata: // * The system/shell/root can see metadata for any app // * An installed app can see metadata for 1) other installed apps // and 2) ephemeral apps that have explicitly interacted with it // * Ephemeral apps can only see their own data and exposed installed apps // * Holding a signature permission allows seeing instant apps - final int callingAppId = UserHandle.getAppId(Binder.getCallingUid()); - if (callingAppId != Process.SYSTEM_UID - && callingAppId != Process.SHELL_UID - && callingAppId != Process.ROOT_UID - && checkUidPermission(Manifest.permission.ACCESS_INSTANT_APPS, - Binder.getCallingUid()) != PackageManager.PERMISSION_GRANTED) { - final String instantAppPackageName = getInstantAppPackageName(Binder.getCallingUid()); + if (!canAccessInstantApps(callingUid)) { + final String instantAppPackageName = getInstantAppPackageName(callingUid); if (instantAppPackageName != null) { // ephemeral apps can only get information on themselves or // installed apps that are exposed. @@ -3512,6 +3533,7 @@ public class PackageManagerService extends IPackageManager.Stub } else { if (ps.getInstantApp(userId)) { // only get access to the ephemeral app if we've been granted access + final int callingAppId = UserHandle.getAppId(callingUid); if (!mInstantAppRegistry.isInstantAccessGranted( userId, callingAppId, ps.appId)) { return null; @@ -23840,6 +23862,11 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName()); return getUidTargetSdkVersionLockedLPr(uid); } } + + @Override + public boolean canAccessInstantApps(int callingUid) { + return PackageManagerService.this.canAccessInstantApps(callingUid); + } } @Override diff --git a/services/core/java/com/android/server/storage/CacheQuotaStrategy.java b/services/core/java/com/android/server/storage/CacheQuotaStrategy.java index 82dd9ace6a5e..7a35bf7bba12 100644 --- a/services/core/java/com/android/server/storage/CacheQuotaStrategy.java +++ b/services/core/java/com/android/server/storage/CacheQuotaStrategy.java @@ -177,7 +177,7 @@ public class CacheQuotaStrategy implements RemoteCallback.OnResultListener { UserInfo info = users.get(i); List<UsageStats> stats = mUsageStats.queryUsageStatsForUser(info.id, UsageStatsManager.INTERVAL_BEST, - oneYearAgo, timeNow); + oneYearAgo, timeNow, /*obfuscateInstantApps=*/ false); if (stats == null) { continue; } diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java index 4ba457d211a3..0de3c7c75016 100644 --- a/services/usage/java/com/android/server/usage/UsageStatsService.java +++ b/services/usage/java/com/android/server/usage/UsageStatsService.java @@ -39,6 +39,7 @@ import android.content.IntentFilter; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; +import android.content.pm.PackageManagerInternal; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ParceledListSlice; import android.content.pm.UserInfo; @@ -80,6 +81,7 @@ import com.android.internal.os.SomeArgs; import com.android.internal.util.ArrayUtils; import com.android.internal.util.DumpUtils; import com.android.internal.util.IndentingPrintWriter; +import com.android.server.LocalServices; import com.android.server.SystemService; import java.io.File; @@ -137,6 +139,7 @@ public class UsageStatsService extends SystemService implements AppOpsManager mAppOps; UserManager mUserManager; PackageManager mPackageManager; + PackageManagerInternal mPackageManagerInternal; AppWidgetManager mAppWidgetManager; IDeviceIdleController mDeviceIdleController; private DisplayManager mDisplayManager; @@ -179,6 +182,7 @@ public class UsageStatsService extends SystemService implements mAppOps = (AppOpsManager) getContext().getSystemService(Context.APP_OPS_SERVICE); mUserManager = (UserManager) getContext().getSystemService(Context.USER_SERVICE); mPackageManager = getContext().getPackageManager(); + mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class); mHandler = new H(BackgroundThread.get().getLooper()); File systemDataDir = new File(Environment.getDataDirectory(), "system"); @@ -407,6 +411,10 @@ public class UsageStatsService extends SystemService implements } } + private boolean shouldObfuscateInstantAppsForCaller(int callingUid) { + return !mPackageManagerInternal.canAccessInstantApps(callingUid); + } + void clearAppIdleForPackage(String packageName, int userId) { synchronized (mAppIdleLock) { mAppIdleHistory.clearUsage(packageName, userId); @@ -704,6 +712,11 @@ public class UsageStatsService extends SystemService implements final long elapsedRealtime = SystemClock.elapsedRealtime(); convertToSystemTimeLocked(event); + if (event.getPackageName() != null + && mPackageManagerInternal.isPackageEphemeral(userId, event.getPackageName())) { + event.mFlags |= Event.FLAG_IS_PACKAGE_INSTANT_APP; + } + final UserUsageStatsService service = getUserDataAndInitializeIfNeededLocked(userId, timeNow); service.reportEvent(event); @@ -807,7 +820,8 @@ public class UsageStatsService extends SystemService implements /** * Called by the Binder stub. */ - List<UsageStats> queryUsageStats(int userId, int bucketType, long beginTime, long endTime) { + List<UsageStats> queryUsageStats(int userId, int bucketType, long beginTime, long endTime, + boolean obfuscateInstantApps) { synchronized (mLock) { final long timeNow = checkAndGetTimeLocked(); if (!validRange(timeNow, beginTime, endTime)) { @@ -816,7 +830,20 @@ public class UsageStatsService extends SystemService implements final UserUsageStatsService service = getUserDataAndInitializeIfNeededLocked(userId, timeNow); - return service.queryUsageStats(bucketType, beginTime, endTime); + List<UsageStats> list = service.queryUsageStats(bucketType, beginTime, endTime); + + // Mangle instant app names *using their current state (not whether they were ephemeral + // when the data was recorded)*. + if (obfuscateInstantApps) { + for (int i = list.size() - 1; i >= 0; i--) { + final UsageStats stats = list.get(i); + if (mPackageManagerInternal.isPackageEphemeral(userId, stats.mPackageName)) { + list.set(i, stats.getObfuscatedForInstantApp()); + } + } + } + + return list; } } @@ -840,7 +867,8 @@ public class UsageStatsService extends SystemService implements /** * Called by the Binder stub. */ - UsageEvents queryEvents(int userId, long beginTime, long endTime) { + UsageEvents queryEvents(int userId, long beginTime, long endTime, + boolean shouldObfuscateInstantApps) { synchronized (mLock) { final long timeNow = checkAndGetTimeLocked(); if (!validRange(timeNow, beginTime, endTime)) { @@ -849,7 +877,7 @@ public class UsageStatsService extends SystemService implements final UserUsageStatsService service = getUserDataAndInitializeIfNeededLocked(userId, timeNow); - return service.queryEvents(beginTime, endTime); + return service.queryEvents(beginTime, endTime, shouldObfuscateInstantApps); } } @@ -884,10 +912,15 @@ public class UsageStatsService extends SystemService implements } } - boolean isAppIdleFilteredOrParoled(String packageName, int userId, long elapsedRealtime) { + boolean isAppIdleFilteredOrParoled(String packageName, int userId, long elapsedRealtime, + boolean shouldObfuscateInstantApps) { if (isParoledOrCharging()) { return false; } + if (shouldObfuscateInstantApps && + mPackageManagerInternal.isPackageEphemeral(userId, packageName)) { + return false; + } return isAppIdleFiltered(packageName, getAppId(packageName), userId, elapsedRealtime); } @@ -1353,11 +1386,14 @@ public class UsageStatsService extends SystemService implements return null; } + final boolean obfuscateInstantApps = shouldObfuscateInstantAppsForCaller( + Binder.getCallingUid()); + final int userId = UserHandle.getCallingUserId(); final long token = Binder.clearCallingIdentity(); try { final List<UsageStats> results = UsageStatsService.this.queryUsageStats( - userId, bucketType, beginTime, endTime); + userId, bucketType, beginTime, endTime, obfuscateInstantApps); if (results != null) { return new ParceledListSlice<>(results); } @@ -1395,10 +1431,14 @@ public class UsageStatsService extends SystemService implements return null; } + final boolean obfuscateInstantApps = shouldObfuscateInstantAppsForCaller( + Binder.getCallingUid()); + final int userId = UserHandle.getCallingUserId(); final long token = Binder.clearCallingIdentity(); try { - return UsageStatsService.this.queryEvents(userId, beginTime, endTime); + return UsageStatsService.this.queryEvents(userId, beginTime, endTime, + obfuscateInstantApps); } finally { Binder.restoreCallingIdentity(token); } @@ -1412,10 +1452,12 @@ public class UsageStatsService extends SystemService implements } catch (RemoteException re) { throw re.rethrowFromSystemServer(); } + final boolean obfuscateInstantApps = shouldObfuscateInstantAppsForCaller( + Binder.getCallingUid()); final long token = Binder.clearCallingIdentity(); try { return UsageStatsService.this.isAppIdleFilteredOrParoled(packageName, userId, - SystemClock.elapsedRealtime()); + SystemClock.elapsedRealtime(), obfuscateInstantApps); } finally { Binder.restoreCallingIdentity(token); } @@ -1647,9 +1689,10 @@ public class UsageStatsService extends SystemService implements @Override public List<UsageStats> queryUsageStatsForUser( - int userId, int intervalType, long beginTime, long endTime) { + int userId, int intervalType, long beginTime, long endTime, + boolean obfuscateInstantApps) { return UsageStatsService.this.queryUsageStats( - userId, intervalType, beginTime, endTime); + userId, intervalType, beginTime, endTime, obfuscateInstantApps); } } } diff --git a/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java b/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java index 96f3305e2538..cc53a9cc0b70 100644 --- a/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java +++ b/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java @@ -26,7 +26,10 @@ import android.app.usage.TimeSparseArray; import android.app.usage.UsageEvents; import android.app.usage.UsageStats; import android.content.res.Configuration; +import android.text.TextUtils; import android.util.ArrayMap; +import android.util.Log; +import android.util.LogWriter; import java.io.IOException; import java.net.ProtocolException; @@ -35,6 +38,8 @@ import java.net.ProtocolException; * UsageStats reader/writer for version 1 of the XML format. */ final class UsageStatsXmlV1 { + private static final String TAG = "UsageStatsXmlV1"; + private static final String PACKAGES_TAG = "packages"; private static final String PACKAGE_TAG = "package"; @@ -51,6 +56,7 @@ final class UsageStatsXmlV1 { // Attributes private static final String PACKAGE_ATTR = "package"; + private static final String FLAGS_ATTR = "flags"; private static final String CLASS_ATTR = "class"; private static final String TOTAL_TIME_ACTIVE_ATTR = "timeActive"; private static final String COUNT_ATTR = "count"; @@ -70,7 +76,6 @@ final class UsageStatsXmlV1 { if (pkg == null) { throw new ProtocolException("no " + PACKAGE_ATTR + " attribute present"); } - final UsageStats stats = statsOut.getOrCreateUsageStats(pkg); // Apply the offset to the beginTime to find the absolute time. @@ -149,11 +154,12 @@ final class UsageStatsXmlV1 { if (packageName == null) { throw new ProtocolException("no " + PACKAGE_ATTR + " attribute present"); } - final String className = XmlUtils.readStringAttribute(parser, CLASS_ATTR); final UsageEvents.Event event = statsOut.buildEvent(packageName, className); + event.mFlags = XmlUtils.readIntAttribute(parser, FLAGS_ATTR, 0); + // Apply the offset to the beginTime to find the absolute time of this event. event.mTimeStamp = statsOut.beginTime + XmlUtils.readLongAttribute(parser, TIME_ATTR); @@ -256,6 +262,7 @@ final class UsageStatsXmlV1 { if (event.mClass != null) { XmlUtils.writeStringAttribute(xml, CLASS_ATTR, event.mClass); } + XmlUtils.writeIntAttribute(xml, FLAGS_ATTR, event.mFlags); XmlUtils.writeIntAttribute(xml, TYPE_ATTR, event.mEventType); switch (event.mEventType) { diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java index 8d335a51bb55..0abbb82a68a1 100644 --- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java +++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java @@ -19,11 +19,8 @@ package com.android.server.usage; import android.app.usage.ConfigurationStats; import android.app.usage.TimeSparseArray; import android.app.usage.UsageEvents; -import android.app.usage.UsageEvents.Event; import android.app.usage.UsageStats; import android.app.usage.UsageStatsManager; -import android.content.pm.PackageInfo; -import android.content.pm.PackageManager; import android.content.res.Configuration; import android.os.SystemClock; import android.content.Context; @@ -312,7 +309,8 @@ class UserUsageStatsService { return queryStats(bucketType, beginTime, endTime, sConfigStatsCombiner); } - UsageEvents queryEvents(final long beginTime, final long endTime) { + UsageEvents queryEvents(final long beginTime, final long endTime, + boolean obfuscateInstantApps) { final ArraySet<String> names = new ArraySet<>(); List<UsageEvents.Event> results = queryStats(UsageStatsManager.INTERVAL_DAILY, beginTime, endTime, new StatCombiner<UsageEvents.Event>() { @@ -334,7 +332,10 @@ class UserUsageStatsService { return; } - final UsageEvents.Event event = stats.events.valueAt(i); + UsageEvents.Event event = stats.events.valueAt(i); + if (obfuscateInstantApps) { + event = event.getObfuscatedIfInstantApp(); + } names.add(event.mPackage); if (event.mClass != null) { names.add(event.mClass); @@ -586,6 +587,7 @@ class UserUsageStatsService { if (event.mShortcutId != null) { pw.printPair("shortcutId", event.mShortcutId); } + pw.printHexPair("flags", event.mFlags); pw.println(); } pw.decreaseIndent(); diff --git a/tests/UsageStatsTest/Android.mk b/tests/UsageStatsTest/Android.mk index 5f7467a301d2..6b5c9998fc06 100644 --- a/tests/UsageStatsTest/Android.mk +++ b/tests/UsageStatsTest/Android.mk @@ -8,6 +8,8 @@ LOCAL_SRC_FILES := $(call all-java-files-under, src) LOCAL_STATIC_JAVA_LIBRARIES := android-support-v4 +LOCAL_CERTIFICATE := platform + LOCAL_PACKAGE_NAME := UsageStatsTest include $(BUILD_PACKAGE) diff --git a/tests/UsageStatsTest/AndroidManifest.xml b/tests/UsageStatsTest/AndroidManifest.xml index 589674a8c0ad..c27be7b2d5bf 100644 --- a/tests/UsageStatsTest/AndroidManifest.xml +++ b/tests/UsageStatsTest/AndroidManifest.xml @@ -1,7 +1,13 @@ <?xml version="1.0" encoding="utf-8"?> +<!-- + Note: Add android:sharedUserId="android.uid.system" to the root element to simulate the system UID + caller case. +--> + <manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.tests.usagestats"> + package="com.android.tests.usagestats" + > <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" /> diff --git a/tests/UsageStatsTest/res/menu/main.xml b/tests/UsageStatsTest/res/menu/main.xml index e781058f1e42..4ccbc81ab317 100644 --- a/tests/UsageStatsTest/res/menu/main.xml +++ b/tests/UsageStatsTest/res/menu/main.xml @@ -2,4 +2,6 @@ <menu xmlns:android="http://schemas.android.com/apk/res/android"> <item android:id="@+id/log" android:title="View Log"/> + <item android:id="@+id/call_is_app_inactive" + android:title="Call isAppInactive()"/> </menu> diff --git a/tests/UsageStatsTest/src/com/android/tests/usagestats/UsageStatsActivity.java b/tests/UsageStatsTest/src/com/android/tests/usagestats/UsageStatsActivity.java index c08c1a3f61c8..9429d9bbf89b 100644 --- a/tests/UsageStatsTest/src/com/android/tests/usagestats/UsageStatsActivity.java +++ b/tests/UsageStatsTest/src/com/android/tests/usagestats/UsageStatsActivity.java @@ -16,12 +16,16 @@ package com.android.tests.usagestats; +import android.app.AlertDialog; import android.app.ListActivity; import android.app.usage.UsageStats; import android.app.usage.UsageStatsManager; import android.content.Context; +import android.content.DialogInterface; import android.content.Intent; import android.os.Bundle; +import android.text.InputType; +import android.text.TextUtils; import android.text.format.DateUtils; import android.view.LayoutInflater; import android.view.Menu; @@ -30,6 +34,7 @@ import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; +import android.widget.EditText; import android.widget.TextView; import java.util.ArrayList; @@ -69,6 +74,9 @@ public class UsageStatsActivity extends ListActivity { case R.id.log: startActivity(new Intent(this, UsageLogActivity.class)); return true; + case R.id.call_is_app_inactive: + callIsAppInactive(); + return true; default: return super.onOptionsItemSelected(item); @@ -81,6 +89,41 @@ public class UsageStatsActivity extends ListActivity { updateAdapter(); } + private void callIsAppInactive() { + final AlertDialog.Builder builder = new AlertDialog.Builder(this); + builder.setTitle("Enter package name"); + final EditText input = new EditText(this); + input.setInputType(InputType.TYPE_CLASS_TEXT); + input.setHint("com.android.tests.usagestats"); + builder.setView(input); + + builder.setPositiveButton("OK", new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + final String packageName = input.getText().toString().trim(); + if (!TextUtils.isEmpty(packageName)) { + showInactive(packageName); + } + } + }); + builder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + dialog.cancel(); + } + }); + + builder.show(); + } + + private void showInactive(String packageName) { + final AlertDialog.Builder builder = new AlertDialog.Builder(this); + builder.setMessage( + "isAppInactive(\"" + packageName + "\") = " + + (mUsageStatsManager.isAppInactive(packageName) ? "true" : "false")); + builder.show(); + } + private void updateAdapter() { long now = System.currentTimeMillis(); long beginTime = now - USAGE_STATS_PERIOD; |