Merge "Activity interceptor dialog for suspended apps" into pi-dev

This commit is contained in:
TreeHugger Robot
2018-04-05 21:15:19 +00:00
committed by Android (Google) Code Review
25 changed files with 525 additions and 75 deletions

View File

@@ -163,6 +163,7 @@ package android {
field public static final java.lang.String REVOKE_RUNTIME_PERMISSIONS = "android.permission.REVOKE_RUNTIME_PERMISSIONS"; field public static final java.lang.String REVOKE_RUNTIME_PERMISSIONS = "android.permission.REVOKE_RUNTIME_PERMISSIONS";
field public static final java.lang.String SCORE_NETWORKS = "android.permission.SCORE_NETWORKS"; field public static final java.lang.String SCORE_NETWORKS = "android.permission.SCORE_NETWORKS";
field public static final java.lang.String SEND_RESPOND_VIA_MESSAGE = "android.permission.SEND_RESPOND_VIA_MESSAGE"; field public static final java.lang.String SEND_RESPOND_VIA_MESSAGE = "android.permission.SEND_RESPOND_VIA_MESSAGE";
field public static final java.lang.String SEND_SHOW_SUSPENDED_APP_DETAILS = "android.permission.SEND_SHOW_SUSPENDED_APP_DETAILS";
field public static final java.lang.String SEND_SMS_NO_CONFIRMATION = "android.permission.SEND_SMS_NO_CONFIRMATION"; field public static final java.lang.String SEND_SMS_NO_CONFIRMATION = "android.permission.SEND_SMS_NO_CONFIRMATION";
field public static final java.lang.String SERIAL_PORT = "android.permission.SERIAL_PORT"; field public static final java.lang.String SERIAL_PORT = "android.permission.SERIAL_PORT";
field public static final java.lang.String SET_ACTIVITY_WATCHER = "android.permission.SET_ACTIVITY_WATCHER"; field public static final java.lang.String SET_ACTIVITY_WATCHER = "android.permission.SET_ACTIVITY_WATCHER";
@@ -878,6 +879,7 @@ package android.content {
field public static final java.lang.String ACTION_QUERY_PACKAGE_RESTART = "android.intent.action.QUERY_PACKAGE_RESTART"; field public static final java.lang.String ACTION_QUERY_PACKAGE_RESTART = "android.intent.action.QUERY_PACKAGE_RESTART";
field public static final java.lang.String ACTION_RESOLVE_INSTANT_APP_PACKAGE = "android.intent.action.RESOLVE_INSTANT_APP_PACKAGE"; field public static final java.lang.String ACTION_RESOLVE_INSTANT_APP_PACKAGE = "android.intent.action.RESOLVE_INSTANT_APP_PACKAGE";
field public static final java.lang.String ACTION_REVIEW_PERMISSIONS = "android.intent.action.REVIEW_PERMISSIONS"; field public static final java.lang.String ACTION_REVIEW_PERMISSIONS = "android.intent.action.REVIEW_PERMISSIONS";
field public static final java.lang.String ACTION_SHOW_SUSPENDED_APP_DETAILS = "android.intent.action.SHOW_SUSPENDED_APP_DETAILS";
field public static final deprecated java.lang.String ACTION_SIM_STATE_CHANGED = "android.intent.action.SIM_STATE_CHANGED"; field public static final deprecated java.lang.String ACTION_SIM_STATE_CHANGED = "android.intent.action.SIM_STATE_CHANGED";
field public static final java.lang.String ACTION_SPLIT_CONFIGURATION_CHANGED = "android.intent.action.SPLIT_CONFIGURATION_CHANGED"; field public static final java.lang.String ACTION_SPLIT_CONFIGURATION_CHANGED = "android.intent.action.SPLIT_CONFIGURATION_CHANGED";
field public static final java.lang.String ACTION_UPGRADE_SETUP = "android.intent.action.UPGRADE_SETUP"; field public static final java.lang.String ACTION_UPGRADE_SETUP = "android.intent.action.UPGRADE_SETUP";

View File

@@ -2155,10 +2155,10 @@ public class ApplicationPackageManager extends PackageManager {
public String[] setPackagesSuspended(String[] packageNames, boolean suspended, public String[] setPackagesSuspended(String[] packageNames, boolean suspended,
PersistableBundle appExtras, PersistableBundle launcherExtras, PersistableBundle appExtras, PersistableBundle launcherExtras,
String dialogMessage) { String dialogMessage) {
// TODO (b/75332201): Pass in the dialogMessage and use it in the interceptor dialog
try { try {
return mPM.setPackagesSuspendedAsUser(packageNames, suspended, appExtras, return mPM.setPackagesSuspendedAsUser(packageNames, suspended, appExtras,
launcherExtras, mContext.getOpPackageName(), mContext.getUserId()); launcherExtras, dialogMessage, mContext.getOpPackageName(),
mContext.getUserId());
} catch (RemoteException e) { } catch (RemoteException e) {
throw e.rethrowFromSystemServer(); throw e.rethrowFromSystemServer();
} }

View File

@@ -2281,6 +2281,28 @@ public class Intent implements Parcelable, Cloneable {
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
public static final String ACTION_MY_PACKAGE_SUSPENDED = "android.intent.action.MY_PACKAGE_SUSPENDED"; public static final String ACTION_MY_PACKAGE_SUSPENDED = "android.intent.action.MY_PACKAGE_SUSPENDED";
/**
* Activity Action: Started to show more details about why an application was suspended.
*
* <p>Apps holding {@link android.Manifest.permission#SUSPEND_APPS} must declare an activity
* handling this intent and protect it with
* {@link android.Manifest.permission#SEND_SHOW_SUSPENDED_APP_DETAILS}.
*
* <p>Includes an extra {@link #EXTRA_PACKAGE_NAME} which is the name of the suspended package.
*
* <p class="note">This is a protected intent that can only be sent
* by the system.
*
* @see PackageManager#isPackageSuspended()
* @see #ACTION_PACKAGES_SUSPENDED
*
* @hide
*/
@SystemApi
@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
public static final String ACTION_SHOW_SUSPENDED_APP_DETAILS =
"android.intent.action.SHOW_SUSPENDED_APP_DETAILS";
/** /**
* Broadcast Action: Sent to a package that has been unsuspended. * Broadcast Action: Sent to a package that has been unsuspended.
* *

View File

@@ -273,8 +273,8 @@ interface IPackageManager {
void clearCrossProfileIntentFilters(int sourceUserId, String ownerPackage); void clearCrossProfileIntentFilters(int sourceUserId, String ownerPackage);
String[] setPackagesSuspendedAsUser(in String[] packageNames, boolean suspended, String[] setPackagesSuspendedAsUser(in String[] packageNames, boolean suspended,
in PersistableBundle launcherExtras, in PersistableBundle appExtras, in PersistableBundle appExtras, in PersistableBundle launcherExtras,
String callingPackage, int userId); String dialogMessage, String callingPackage, int userId);
boolean isPackageSuspendedForUser(String packageName, int userId); boolean isPackageSuspendedForUser(String packageName, int userId);

View File

@@ -191,10 +191,10 @@ public abstract class PackageManagerInternal {
/** /**
* Retrieve launcher extras for a suspended package provided to the system in * Retrieve launcher extras for a suspended package provided to the system in
* {@link PackageManager#setPackagesSuspended(String[], boolean, PersistableBundle, * {@link PackageManager#setPackagesSuspended(String[], boolean, PersistableBundle,
* PersistableBundle, String)} * PersistableBundle, String)}.
* *
* @param packageName The package for which to return launcher extras. * @param packageName The package for which to return launcher extras.
* @param userId The user for which to check, * @param userId The user for which to check.
* @return The launcher extras. * @return The launcher extras.
* *
* @see PackageManager#setPackagesSuspended(String[], boolean, PersistableBundle, * @see PackageManager#setPackagesSuspended(String[], boolean, PersistableBundle,
@@ -213,6 +213,29 @@ public abstract class PackageManagerInternal {
*/ */
public abstract boolean isPackageSuspended(String packageName, int userId); public abstract boolean isPackageSuspended(String packageName, int userId);
/**
* Get the name of the package that suspended the given package. Packages can be suspended by
* device administrators or apps holding {@link android.Manifest.permission#MANAGE_USERS} or
* {@link android.Manifest.permission#SUSPEND_APPS}.
*
* @param suspendedPackage The package that has been suspended.
* @param userId The user for which to check.
* @return Name of the package that suspended the given package. Returns {@code null} if the
* given package is not currently suspended and the platform package name - i.e.
* {@code "android"} - if the package was suspended by a device admin.
*/
public abstract String getSuspendingPackage(String suspendedPackage, int userId);
/**
* Get the dialog message to be shown to the user when they try to launch a suspended
* application.
*
* @param suspendedPackage The package that has been suspended.
* @param userId The user for which to check.
* @return The dialog message to be shown to the user.
*/
public abstract String getSuspendedDialogMessage(String suspendedPackage, int userId);
/** /**
* Do a straight uid lookup for the given package/application in the given user. * Do a straight uid lookup for the given package/application in the given user.
* @see PackageManager#getPackageUidAsUser(String, int, int) * @see PackageManager#getPackageUidAsUser(String, int, int)

View File

@@ -34,6 +34,7 @@ import android.util.ArraySet;
import com.android.internal.util.ArrayUtils; import com.android.internal.util.ArrayUtils;
import java.util.Arrays; import java.util.Arrays;
import java.util.Objects;
/** /**
* Per-user state information about a package. * Per-user state information about a package.
@@ -47,6 +48,7 @@ public class PackageUserState {
public boolean hidden; // Is the app restricted by owner / admin public boolean hidden; // Is the app restricted by owner / admin
public boolean suspended; public boolean suspended;
public String suspendingPackage; public String suspendingPackage;
public String dialogMessage; // Message to show when a suspended package launch attempt is made
public PersistableBundle suspendedAppExtras; public PersistableBundle suspendedAppExtras;
public PersistableBundle suspendedLauncherExtras; public PersistableBundle suspendedLauncherExtras;
public boolean instantApp; public boolean instantApp;
@@ -82,6 +84,7 @@ public class PackageUserState {
hidden = o.hidden; hidden = o.hidden;
suspended = o.suspended; suspended = o.suspended;
suspendingPackage = o.suspendingPackage; suspendingPackage = o.suspendingPackage;
dialogMessage = o.dialogMessage;
suspendedAppExtras = o.suspendedAppExtras; suspendedAppExtras = o.suspendedAppExtras;
suspendedLauncherExtras = o.suspendedLauncherExtras; suspendedLauncherExtras = o.suspendedLauncherExtras;
instantApp = o.instantApp; instantApp = o.instantApp;
@@ -208,6 +211,9 @@ public class PackageUserState {
|| !suspendingPackage.equals(oldState.suspendingPackage)) { || !suspendingPackage.equals(oldState.suspendingPackage)) {
return false; return false;
} }
if (!Objects.equals(dialogMessage, oldState.dialogMessage)) {
return false;
}
if (!BaseBundle.kindofEquals(suspendedAppExtras, if (!BaseBundle.kindofEquals(suspendedAppExtras,
oldState.suspendedAppExtras)) { oldState.suspendedAppExtras)) {
return false; return false;

View File

@@ -0,0 +1,82 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.internal.app;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.os.UserHandle;
import android.util.Slog;
import android.view.Window;
import android.view.WindowManager;
import com.android.internal.R;
public class SuspendedAppActivity extends AlertActivity
implements DialogInterface.OnClickListener {
private static final String TAG = "SuspendedAppActivity";
public static final String EXTRA_DIALOG_MESSAGE = "SuspendedAppActivity.extra.DIALOG_MESSAGE";
public static final String EXTRA_MORE_DETAILS_INTENT =
"SuspendedAppActivity.extra.MORE_DETAILS_INTENT";
private Intent mMoreDetailsIntent;
private int mUserId;
@Override
public void onCreate(Bundle icicle) {
Window window = getWindow();
window.setType(WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG);
super.onCreate(icicle);
final Intent intent = getIntent();
mMoreDetailsIntent = intent.getParcelableExtra(EXTRA_MORE_DETAILS_INTENT);
mUserId = intent.getIntExtra(Intent.EXTRA_USER_ID, -1);
if (mUserId < 0) {
Slog.wtf(TAG, "Invalid user: " + mUserId);
finish();
return;
}
String dialogMessage = intent.getStringExtra(EXTRA_DIALOG_MESSAGE);
if (dialogMessage == null) {
dialogMessage = getString(R.string.app_suspended_default_message);
}
final AlertController.AlertParams ap = mAlertParams;
ap.mTitle = getString(R.string.app_suspended_title);
ap.mMessage = String.format(getResources().getConfiguration().getLocales().get(0),
dialogMessage, intent.getStringExtra(Intent.EXTRA_PACKAGE_NAME));
ap.mPositiveButtonText = getString(android.R.string.ok);
if (mMoreDetailsIntent != null) {
ap.mNeutralButtonText = getString(R.string.app_suspended_more_details);
}
ap.mPositiveButtonListener = ap.mNeutralButtonListener = this;
setupAlert();
}
@Override
public void onClick(DialogInterface dialog, int which) {
switch (which) {
case AlertDialog.BUTTON_NEUTRAL:
startActivityAsUser(mMoreDetailsIntent, UserHandle.of(mUserId));
Slog.i(TAG, "Started more details activity");
break;
}
finish();
}
}

View File

@@ -2030,6 +2030,15 @@
<permission android:name="android.permission.START_ANY_ACTIVITY" <permission android:name="android.permission.START_ANY_ACTIVITY"
android:protectionLevel="signature" /> android:protectionLevel="signature" />
<!-- @SystemApi Must be required by activities that handle the intent action
{@link Intent#ACTION_SEND_SHOW_SUSPENDED_APP_DETAILS}. This is for use by apps that
hold {@link Manifest.permission#SUSPEND_APPS} to interact with the system.
<p>Not for use by third-party applications.
@hide -->
<permission android:name="android.permission.SEND_SHOW_SUSPENDED_APP_DETAILS"
android:protectionLevel="signature" />
<uses-permission android:name="android.permission.SEND_SHOW_SUSPENDED_APP_DETAILS" />
<!-- @deprecated The {@link android.app.ActivityManager#restartPackage} <!-- @deprecated The {@link android.app.ActivityManager#restartPackage}
API is no longer supported. --> API is no longer supported. -->
<permission android:name="android.permission.RESTART_PACKAGES" <permission android:name="android.permission.RESTART_PACKAGES"
@@ -4116,6 +4125,12 @@
</intent-filter> </intent-filter>
</activity> </activity>
<activity android:name="com.android.internal.app.SuspendedAppActivity"
android:theme="@style/Theme.DeviceDefault.Light.Dialog.Alert"
android:excludeFromRecents="true"
android:process=":ui">
</activity>
<activity android:name="com.android.internal.app.UnlaunchableAppActivity" <activity android:name="com.android.internal.app.UnlaunchableAppActivity"
android:theme="@style/Theme.DeviceDefault.Light.Dialog.Alert" android:theme="@style/Theme.DeviceDefault.Light.Dialog.Alert"
android:excludeFromRecents="true" android:excludeFromRecents="true"

View File

@@ -4703,6 +4703,13 @@
<!-- Menu item in the locale menu [CHAR LIMIT=30] --> <!-- Menu item in the locale menu [CHAR LIMIT=30] -->
<string name="locale_search_menu">Search</string> <string name="locale_search_menu">Search</string>
<!-- Title of the dialog that is shown when the user tries to launch a suspended application [CHAR LIMIT=30] -->
<string name="app_suspended_title">Action not allowed</string>
<!-- Default message shown in the dialog that is shown when the user tries to launch a suspended application [CHAR LIMIT=NONE] -->
<string name="app_suspended_default_message">The application <xliff:g id="app_name" example="GMail">%1$s</xliff:g> is currently disabled.</string>
<!-- Title of the button to show users more details about why the app has been suspended [CHAR LIMIT=50]-->
<string name="app_suspended_more_details">More details</string>
<!-- Title of a dialog. The string is asking if the user wants to turn on their work profile, which contains work apps that are managed by their employer. "Work" is an adjective. [CHAR LIMIT=30] --> <!-- Title of a dialog. The string is asking if the user wants to turn on their work profile, which contains work apps that are managed by their employer. "Work" is an adjective. [CHAR LIMIT=30] -->
<string name="work_mode_off_title">Turn on work profile?</string> <string name="work_mode_off_title">Turn on work profile?</string>
<!-- Text in a dialog. This string describes what will happen if a user decides to turn on their work profile. "Work profile" is used as an adjective. [CHAR LIMIT=NONE] --> <!-- Text in a dialog. This string describes what will happen if a user decides to turn on their work profile. "Work profile" is used as an adjective. [CHAR LIMIT=NONE] -->

View File

@@ -2877,6 +2877,10 @@
<java-symbol type="string" name="suspended_widget_accessibility" /> <java-symbol type="string" name="suspended_widget_accessibility" />
<java-symbol type="string" name="app_suspended_title" />
<java-symbol type="string" name="app_suspended_more_details" />
<java-symbol type="string" name="app_suspended_default_message" />
<!-- Used internally for assistant to launch activity transitions --> <!-- Used internally for assistant to launch activity transitions -->
<java-symbol type="id" name="cross_task_transition" /> <java-symbol type="id" name="cross_task_transition" />

View File

@@ -32,6 +32,9 @@ import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.content.Intent.FLAG_ACTIVITY_TASK_ON_HOME; import static android.content.Intent.FLAG_ACTIVITY_TASK_ON_HOME;
import static android.content.pm.ApplicationInfo.FLAG_SUSPENDED; import static android.content.pm.ApplicationInfo.FLAG_SUSPENDED;
import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
import android.Manifest;
import android.app.ActivityOptions; import android.app.ActivityOptions;
import android.app.AppGlobals; import android.app.AppGlobals;
import android.app.KeyguardManager; import android.app.KeyguardManager;
@@ -41,6 +44,7 @@ import android.content.IIntentSender;
import android.content.Intent; import android.content.Intent;
import android.content.IntentSender; import android.content.IntentSender;
import android.content.pm.ActivityInfo; import android.content.pm.ActivityInfo;
import android.content.pm.PackageManagerInternal;
import android.content.pm.ResolveInfo; import android.content.pm.ResolveInfo;
import android.content.pm.UserInfo; import android.content.pm.UserInfo;
import android.os.Binder; import android.os.Binder;
@@ -51,6 +55,7 @@ import android.os.UserManager;
import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.HarmfulAppWarningActivity; import com.android.internal.app.HarmfulAppWarningActivity;
import com.android.internal.app.SuspendedAppActivity;
import com.android.internal.app.UnlaunchableAppActivity; import com.android.internal.app.UnlaunchableAppActivity;
import com.android.server.LocalServices; import com.android.server.LocalServices;
@@ -151,7 +156,7 @@ class ActivityStartInterceptor {
mInTask = inTask; mInTask = inTask;
mActivityOptions = activityOptions; mActivityOptions = activityOptions;
if (interceptSuspendPackageIfNeed()) { if (interceptSuspendedPackageIfNeeded()) {
// Skip the rest of interceptions as the package is suspended by device admin so // Skip the rest of interceptions as the package is suspended by device admin so
// no user action can undo this. // no user action can undo this.
return true; return true;
@@ -204,12 +209,7 @@ class ActivityStartInterceptor {
return true; return true;
} }
private boolean interceptSuspendPackageIfNeed() { private boolean interceptSuspendedByAdminPackage() {
// Do not intercept if the admin did not suspend the package
if (mAInfo == null || mAInfo.applicationInfo == null ||
(mAInfo.applicationInfo.flags & FLAG_SUSPENDED) == 0) {
return false;
}
DevicePolicyManagerInternal devicePolicyManager = LocalServices DevicePolicyManagerInternal devicePolicyManager = LocalServices
.getService(DevicePolicyManagerInternal.class); .getService(DevicePolicyManagerInternal.class);
if (devicePolicyManager == null) { if (devicePolicyManager == null) {
@@ -232,6 +232,55 @@ class ActivityStartInterceptor {
return true; return true;
} }
private Intent createSuspendedAppInterceptIntent(String suspendedPackage,
String suspendingPackage, String dialogMessage, int userId) {
final Intent interceptIntent = new Intent(mServiceContext, SuspendedAppActivity.class)
.putExtra(Intent.EXTRA_PACKAGE_NAME, suspendedPackage)
.putExtra(SuspendedAppActivity.EXTRA_DIALOG_MESSAGE, dialogMessage)
.putExtra(Intent.EXTRA_USER_ID, userId)
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
final Intent moreDetailsIntent = new Intent(Intent.ACTION_SHOW_SUSPENDED_APP_DETAILS)
.setPackage(suspendingPackage);
final String requiredPermission = Manifest.permission.SEND_SHOW_SUSPENDED_APP_DETAILS;
final ResolveInfo resolvedInfo = mSupervisor.resolveIntent(moreDetailsIntent, null, userId);
if (resolvedInfo != null && resolvedInfo.activityInfo != null
&& requiredPermission.equals(resolvedInfo.activityInfo.permission)) {
moreDetailsIntent.putExtra(Intent.EXTRA_PACKAGE_NAME, suspendedPackage)
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
interceptIntent.putExtra(SuspendedAppActivity.EXTRA_MORE_DETAILS_INTENT,
moreDetailsIntent);
}
return interceptIntent;
}
private boolean interceptSuspendedPackageIfNeeded() {
// Do not intercept if the package is not suspended
if (mAInfo == null || mAInfo.applicationInfo == null ||
(mAInfo.applicationInfo.flags & FLAG_SUSPENDED) == 0) {
return false;
}
final PackageManagerInternal pmi = mService.getPackageManagerInternalLocked();
if (pmi == null) {
return false;
}
final String suspendedPackage = mAInfo.applicationInfo.packageName;
final String suspendingPackage = pmi.getSuspendingPackage(suspendedPackage, mUserId);
if (PLATFORM_PACKAGE_NAME.equals(suspendingPackage)) {
return interceptSuspendedByAdminPackage();
}
final String dialogMessage = pmi.getSuspendedDialogMessage(suspendedPackage, mUserId);
mIntent = createSuspendedAppInterceptIntent(suspendedPackage, suspendingPackage,
dialogMessage, mUserId);
mCallingPid = mRealCallingPid;
mCallingUid = mRealCallingUid;
mResolvedType = null;
mRInfo = mSupervisor.resolveIntent(mIntent, mResolvedType, 0);
mAInfo = mSupervisor.resolveActivity(mIntent, mRInfo, mStartFlags, null /*profilerInfo*/);
return true;
}
private boolean interceptWorkProfileChallengeIfNeeded() { private boolean interceptWorkProfileChallengeIfNeeded() {
final Intent interceptingIntent = interceptWithConfirmCredentialsIfNeeded(mAInfo, mUserId); final Intent interceptingIntent = interceptWithConfirmCredentialsIfNeeded(mAInfo, mUserId);
if (interceptingIntent == null) { if (interceptingIntent == null) {

View File

@@ -274,6 +274,7 @@ import com.android.internal.R;
import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.GuardedBy;
import com.android.internal.app.IMediaContainerService; import com.android.internal.app.IMediaContainerService;
import com.android.internal.app.ResolverActivity; import com.android.internal.app.ResolverActivity;
import com.android.internal.app.SuspendedAppActivity;
import com.android.internal.content.NativeLibraryHelper; import com.android.internal.content.NativeLibraryHelper;
import com.android.internal.content.PackageHelper; import com.android.internal.content.PackageHelper;
import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.MetricsLogger;
@@ -14006,8 +14007,8 @@ public class PackageManagerService extends IPackageManager.Stub
@Override @Override
public String[] setPackagesSuspendedAsUser(String[] packageNames, boolean suspended, public String[] setPackagesSuspendedAsUser(String[] packageNames, boolean suspended,
PersistableBundle appExtras, PersistableBundle launcherExtras, String callingPackage, PersistableBundle appExtras, PersistableBundle launcherExtras, String dialogMessage,
int userId) { String callingPackage, int userId) {
try { try {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.SUSPEND_APPS, null); mContext.enforceCallingOrSelfPermission(android.Manifest.permission.SUSPEND_APPS, null);
} catch (SecurityException e) { } catch (SecurityException e) {
@@ -14055,7 +14056,7 @@ public class PackageManagerService extends IPackageManager.Stub
unactionedPackages.add(packageName); unactionedPackages.add(packageName);
continue; continue;
} }
pkgSetting.setSuspended(suspended, callingPackage, appExtras, pkgSetting.setSuspended(suspended, callingPackage, dialogMessage, appExtras,
launcherExtras, userId); launcherExtras, userId);
changedPackagesList.add(packageName); changedPackagesList.add(packageName);
} }
@@ -14177,7 +14178,7 @@ public class PackageManagerService extends IPackageManager.Stub
for (int user : userIds) { for (int user : userIds) {
final PackageUserState pus = ps.readUserState(user); final PackageUserState pus = ps.readUserState(user);
if (pus.suspended && packageName.equals(pus.suspendingPackage)) { if (pus.suspended && packageName.equals(pus.suspendingPackage)) {
ps.setSuspended(false, null, null, null, user); ps.setSuspended(false, null, null, null, null, user);
} }
} }
} }
@@ -18911,6 +18912,7 @@ public class PackageManagerService extends IPackageManager.Stub
false /*hidden*/, false /*hidden*/,
false /*suspended*/, false /*suspended*/,
null, /*suspendingPackage*/ null, /*suspendingPackage*/
null, /*dialogMessage*/
null, /*suspendedAppExtras*/ null, /*suspendedAppExtras*/
null, /*suspendedLauncherExtras*/ null, /*suspendedLauncherExtras*/
false /*instantApp*/, false /*instantApp*/,
@@ -23802,6 +23804,22 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName());
} }
} }
@Override
public String getSuspendingPackage(String suspendedPackage, int userId) {
synchronized (mPackages) {
final PackageSetting ps = mSettings.mPackages.get(suspendedPackage);
return (ps != null) ? ps.readUserState(userId).suspendingPackage : null;
}
}
@Override
public String getSuspendedDialogMessage(String suspendedPackage, int userId) {
synchronized (mPackages) {
final PackageSetting ps = mSettings.mPackages.get(suspendedPackage);
return (ps != null) ? ps.readUserState(userId).dialogMessage : null;
}
}
@Override @Override
public int getPackageUid(String packageName, int flags, int userId) { public int getPackageUid(String packageName, int flags, int userId) {
return PackageManagerService.this return PackageManagerService.this

View File

@@ -1505,6 +1505,7 @@ class PackageManagerShellCommand extends ShellCommand {
private int runSuspend(boolean suspendedState) { private int runSuspend(boolean suspendedState) {
final PrintWriter pw = getOutPrintWriter(); final PrintWriter pw = getOutPrintWriter();
int userId = UserHandle.USER_SYSTEM; int userId = UserHandle.USER_SYSTEM;
String dialogMessage = null;
final PersistableBundle appExtras = new PersistableBundle(); final PersistableBundle appExtras = new PersistableBundle();
final PersistableBundle launcherExtras = new PersistableBundle(); final PersistableBundle launcherExtras = new PersistableBundle();
String opt; String opt;
@@ -1513,6 +1514,9 @@ class PackageManagerShellCommand extends ShellCommand {
case "--user": case "--user":
userId = UserHandle.parseUserArg(getNextArgRequired()); userId = UserHandle.parseUserArg(getNextArgRequired());
break; break;
case "--dialogMessage":
dialogMessage = getNextArgRequired();
break;
case "--ael": case "--ael":
case "--aes": case "--aes":
case "--aed": case "--aed":
@@ -1553,7 +1557,7 @@ class PackageManagerShellCommand extends ShellCommand {
(Binder.getCallingUid() == Process.ROOT_UID) ? "root" : "com.android.shell"; (Binder.getCallingUid() == Process.ROOT_UID) ? "root" : "com.android.shell";
try { try {
mInterface.setPackagesSuspendedAsUser(new String[]{packageName}, suspendedState, mInterface.setPackagesSuspendedAsUser(new String[]{packageName}, suspendedState,
appExtras, launcherExtras, callingPackage, userId); appExtras, launcherExtras, dialogMessage, callingPackage, userId);
pw.println("Package " + packageName + " new suspended state: " pw.println("Package " + packageName + " new suspended state: "
+ mInterface.isPackageSuspendedForUser(packageName, userId)); + mInterface.isPackageSuspendedForUser(packageName, userId));
return 0; return 0;

View File

@@ -398,11 +398,12 @@ public abstract class PackageSettingBase extends SettingBase {
return readUserState(userId).suspended; return readUserState(userId).suspended;
} }
void setSuspended(boolean suspended, String suspendingPackage, PersistableBundle appExtras, void setSuspended(boolean suspended, String suspendingPackage, String dialogMessage,
PersistableBundle launcherExtras, int userId) { PersistableBundle appExtras, PersistableBundle launcherExtras, int userId) {
final PackageUserState existingUserState = modifyUserState(userId); final PackageUserState existingUserState = modifyUserState(userId);
existingUserState.suspended = suspended; existingUserState.suspended = suspended;
existingUserState.suspendingPackage = suspended ? suspendingPackage : null; existingUserState.suspendingPackage = suspended ? suspendingPackage : null;
existingUserState.dialogMessage = suspended ? dialogMessage : null;
existingUserState.suspendedAppExtras = suspended ? appExtras : null; existingUserState.suspendedAppExtras = suspended ? appExtras : null;
existingUserState.suspendedLauncherExtras = suspended ? launcherExtras : null; existingUserState.suspendedLauncherExtras = suspended ? launcherExtras : null;
} }
@@ -425,8 +426,8 @@ public abstract class PackageSettingBase extends SettingBase {
void setUserState(int userId, long ceDataInode, int enabled, boolean installed, boolean stopped, void setUserState(int userId, long ceDataInode, int enabled, boolean installed, boolean stopped,
boolean notLaunched, boolean hidden, boolean suspended, String suspendingPackage, boolean notLaunched, boolean hidden, boolean suspended, String suspendingPackage,
PersistableBundle suspendedAppExtras, PersistableBundle suspendedLauncherExtras, String dialogMessage, PersistableBundle suspendedAppExtras,
boolean instantApp, PersistableBundle suspendedLauncherExtras, boolean instantApp,
boolean virtualPreload, String lastDisableAppCaller, boolean virtualPreload, String lastDisableAppCaller,
ArraySet<String> enabledComponents, ArraySet<String> disabledComponents, ArraySet<String> enabledComponents, ArraySet<String> disabledComponents,
int domainVerifState, int linkGeneration, int installReason, int domainVerifState, int linkGeneration, int installReason,
@@ -440,6 +441,7 @@ public abstract class PackageSettingBase extends SettingBase {
state.hidden = hidden; state.hidden = hidden;
state.suspended = suspended; state.suspended = suspended;
state.suspendingPackage = suspendingPackage; state.suspendingPackage = suspendingPackage;
state.dialogMessage = dialogMessage;
state.suspendedAppExtras = suspendedAppExtras; state.suspendedAppExtras = suspendedAppExtras;
state.suspendedLauncherExtras = suspendedLauncherExtras; state.suspendedLauncherExtras = suspendedLauncherExtras;
state.lastDisableAppCaller = lastDisableAppCaller; state.lastDisableAppCaller = lastDisableAppCaller;

View File

@@ -222,6 +222,7 @@ public final class Settings {
private static final String ATTR_HIDDEN = "hidden"; private static final String ATTR_HIDDEN = "hidden";
private static final String ATTR_SUSPENDED = "suspended"; private static final String ATTR_SUSPENDED = "suspended";
private static final String ATTR_SUSPENDING_PACKAGE = "suspending-package"; private static final String ATTR_SUSPENDING_PACKAGE = "suspending-package";
private static final String ATTR_SUSPEND_DIALOG_MESSAGE = "suspend_dialog_message";
// Legacy, uninstall blocks are stored separately. // Legacy, uninstall blocks are stored separately.
@Deprecated @Deprecated
private static final String ATTR_BLOCK_UNINSTALL = "blockUninstall"; private static final String ATTR_BLOCK_UNINSTALL = "blockUninstall";
@@ -734,6 +735,7 @@ public final class Settings {
false /*hidden*/, false /*hidden*/,
false /*suspended*/, false /*suspended*/,
null, /*suspendingPackage*/ null, /*suspendingPackage*/
null, /*dialogMessage*/
null, /*suspendedAppExtras*/ null, /*suspendedAppExtras*/
null, /*suspendedLauncherExtras*/ null, /*suspendedLauncherExtras*/
instantApp, instantApp,
@@ -1628,6 +1630,7 @@ public final class Settings {
false /*hidden*/, false /*hidden*/,
false /*suspended*/, false /*suspended*/,
null, /*suspendingPackage*/ null, /*suspendingPackage*/
null, /*dialogMessage*/
null, /*suspendedAppExtras*/ null, /*suspendedAppExtras*/
null, /*suspendedLauncherExtras*/ null, /*suspendedLauncherExtras*/
false /*instantApp*/, false /*instantApp*/,
@@ -1704,6 +1707,8 @@ public final class Settings {
false); false);
String suspendingPackage = parser.getAttributeValue(null, String suspendingPackage = parser.getAttributeValue(null,
ATTR_SUSPENDING_PACKAGE); ATTR_SUSPENDING_PACKAGE);
final String dialogMessage = parser.getAttributeValue(null,
ATTR_SUSPEND_DIALOG_MESSAGE);
if (suspended && suspendingPackage == null) { if (suspended && suspendingPackage == null) {
suspendingPackage = PLATFORM_PACKAGE_NAME; suspendingPackage = PLATFORM_PACKAGE_NAME;
} }
@@ -1767,7 +1772,7 @@ public final class Settings {
setBlockUninstallLPw(userId, name, true); setBlockUninstallLPw(userId, name, true);
} }
ps.setUserState(userId, ceDataInode, enabled, installed, stopped, notLaunched, ps.setUserState(userId, ceDataInode, enabled, installed, stopped, notLaunched,
hidden, suspended, suspendingPackage, suspendedAppExtras, hidden, suspended, suspendingPackage, dialogMessage, suspendedAppExtras,
suspendedLauncherExtras, instantApp, virtualPreload, enabledCaller, suspendedLauncherExtras, instantApp, virtualPreload, enabledCaller,
enabledComponents, disabledComponents, verifState, linkGeneration, enabledComponents, disabledComponents, verifState, linkGeneration,
installReason, harmfulAppWarning); installReason, harmfulAppWarning);
@@ -2077,7 +2082,14 @@ public final class Settings {
} }
if (ustate.suspended) { if (ustate.suspended) {
serializer.attribute(null, ATTR_SUSPENDED, "true"); serializer.attribute(null, ATTR_SUSPENDED, "true");
serializer.attribute(null, ATTR_SUSPENDING_PACKAGE, ustate.suspendingPackage); if (ustate.suspendingPackage != null) {
serializer.attribute(null, ATTR_SUSPENDING_PACKAGE,
ustate.suspendingPackage);
}
if (ustate.dialogMessage != null) {
serializer.attribute(null, ATTR_SUSPEND_DIALOG_MESSAGE,
ustate.dialogMessage);
}
if (ustate.suspendedAppExtras != null) { if (ustate.suspendedAppExtras != null) {
serializer.startTag(null, TAG_SUSPENDED_APP_EXTRAS); serializer.startTag(null, TAG_SUSPENDED_APP_EXTRAS);
try { try {
@@ -4750,8 +4762,11 @@ public final class Settings {
pw.print(" suspended="); pw.print(" suspended=");
pw.print(ps.getSuspended(user.id)); pw.print(ps.getSuspended(user.id));
if (ps.getSuspended(user.id)) { if (ps.getSuspended(user.id)) {
final PackageUserState pus = ps.readUserState(user.id);
pw.print(" suspendingPackage="); pw.print(" suspendingPackage=");
pw.print(ps.readUserState(user.id).suspendingPackage); pw.print(pus.suspendingPackage);
pw.print(" dialogMessage=");
pw.print(pus.dialogMessage);
} }
pw.print(" stopped="); pw.print(" stopped=");
pw.print(ps.getStopped(user.id)); pw.print(ps.getStopped(user.id));

View File

@@ -9193,7 +9193,7 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
long id = mInjector.binderClearCallingIdentity(); long id = mInjector.binderClearCallingIdentity();
try { try {
return mIPackageManager.setPackagesSuspendedAsUser( return mIPackageManager.setPackagesSuspendedAsUser(
packageNames, suspended, null, null, "android", callingUserId); packageNames, suspended, null, null, null, "android", callingUserId);
} catch (RemoteException re) { } catch (RemoteException re) {
// Shouldn't happen. // Shouldn't happen.
Slog.e(LOG_TAG, "Failed talking to the package manager", re); Slog.e(LOG_TAG, "Failed talking to the package manager", re);

View File

@@ -29,6 +29,7 @@ LOCAL_STATIC_JAVA_LIBRARIES := \
truth-prebuilt \ truth-prebuilt \
testables \ testables \
testng \ testng \
ub-uiautomator\
platformprotosnano platformprotosnano
LOCAL_AIDL_INCLUDES := $(LOCAL_PATH)/aidl LOCAL_AIDL_INCLUDES := $(LOCAL_PATH)/aidl
@@ -69,8 +70,6 @@ LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
LOCAL_JACK_FLAGS := --multi-dex native LOCAL_JACK_FLAGS := --multi-dex native
LOCAL_DX_FLAGS := --multi-dex LOCAL_DX_FLAGS := --multi-dex
LOCAL_STATIC_JAVA_LIBRARIES += ub-uiautomator
LOCAL_PROGUARD_ENABLED := disabled LOCAL_PROGUARD_ENABLED := disabled
include $(BUILD_PACKAGE) include $(BUILD_PACKAGE)

View File

@@ -63,6 +63,7 @@
<uses-permission android:name="android.permission.WATCH_APPOPS" /> <uses-permission android:name="android.permission.WATCH_APPOPS" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/> <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<uses-permission android:name="android.permission.SUSPEND_APPS"/> <uses-permission android:name="android.permission.SUSPEND_APPS"/>
<uses-permission android:name="android.permission.CONTROL_KEYGUARD"/>
<!-- Uses API introduced in O (26) --> <!-- Uses API introduced in O (26) -->
<uses-sdk android:minSdkVersion="1" <uses-sdk android:minSdkVersion="1"
@@ -145,6 +146,15 @@
<activity android:name="com.android.server.pm.ShortcutTestActivity" <activity android:name="com.android.server.pm.ShortcutTestActivity"
android:enabled="true" android:exported="true" /> android:enabled="true" android:exported="true" />
<activity android:name="com.android.server.pm.SuspendedDetailsActivity"
android:enabled="true"
android:permission="android.permission.SEND_SHOW_SUSPENDED_APP_DETAILS">
<intent-filter>
<action android:name="android.intent.action.SHOW_SUSPENDED_APP_DETAILS" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<activity android:name="com.android.server.accounts.AccountAuthenticatorDummyActivity" /> <activity android:name="com.android.server.accounts.AccountAuthenticatorDummyActivity" />
<activity-alias android:name="a.ShortcutEnabled" <activity-alias android:name="a.ShortcutEnabled"

View File

@@ -17,6 +17,9 @@
package com.android.server.am; package com.android.server.am;
import static android.content.pm.ApplicationInfo.FLAG_SUSPENDED; import static android.content.pm.ApplicationInfo.FLAG_SUSPENDED;
import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
@@ -30,6 +33,7 @@ import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.pm.ActivityInfo; import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo; import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManagerInternal;
import android.content.pm.UserInfo; import android.content.pm.UserInfo;
import android.os.UserHandle; import android.os.UserHandle;
import android.os.UserManager; import android.os.UserManager;
@@ -79,6 +83,8 @@ public class ActivityStartInterceptorTest {
@Mock @Mock
private DevicePolicyManagerInternal mDevicePolicyManager; private DevicePolicyManagerInternal mDevicePolicyManager;
@Mock @Mock
private PackageManagerInternal mPackageManagerInternal;
@Mock
private UserManager mUserManager; private UserManager mUserManager;
@Mock @Mock
private UserController mUserController; private UserController mUserController;
@@ -103,6 +109,7 @@ public class ActivityStartInterceptorTest {
when(mDevicePolicyManager when(mDevicePolicyManager
.createShowAdminSupportIntent(TEST_USER_ID, true)) .createShowAdminSupportIntent(TEST_USER_ID, true))
.thenReturn(ADMIN_SUPPORT_INTENT); .thenReturn(ADMIN_SUPPORT_INTENT);
when(mService.getPackageManagerInternalLocked()).thenReturn(mPackageManagerInternal);
// Mock UserManager // Mock UserManager
when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager); when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager);
@@ -115,15 +122,18 @@ public class ActivityStartInterceptorTest {
thenReturn(CONFIRM_CREDENTIALS_INTENT); thenReturn(CONFIRM_CREDENTIALS_INTENT);
// Initialise activity info // Initialise activity info
mAInfo.packageName = TEST_PACKAGE_NAME;
mAInfo.applicationInfo = new ApplicationInfo(); mAInfo.applicationInfo = new ApplicationInfo();
mAInfo.packageName = mAInfo.applicationInfo.packageName = TEST_PACKAGE_NAME;
} }
@Test @Test
public void testSuspendedPackage() { public void testSuspendedByAdminPackage() {
// GIVEN the package we're about to launch is currently suspended // GIVEN the package we're about to launch is currently suspended
mAInfo.applicationInfo.flags = FLAG_SUSPENDED; mAInfo.applicationInfo.flags = FLAG_SUSPENDED;
when(mPackageManagerInternal.getSuspendingPackage(TEST_PACKAGE_NAME, TEST_USER_ID))
.thenReturn(PLATFORM_PACKAGE_NAME);
// THEN calling intercept returns true // THEN calling intercept returns true
assertTrue(mInterceptor.intercept(null, null, mAInfo, null, null, 0, 0, null)); assertTrue(mInterceptor.intercept(null, null, mAInfo, null, null, 0, 0, null));

View File

@@ -199,13 +199,13 @@ public class PackageManagerSettingsTests {
PACKAGE_NAME_1, 1L, 0.01, true, "appString1"); PACKAGE_NAME_1, 1L, 0.01, true, "appString1");
final PersistableBundle launcherExtras1 = getPersistableBundle( final PersistableBundle launcherExtras1 = getPersistableBundle(
PACKAGE_NAME_1, 10L, 0.1, false, "launcherString1"); PACKAGE_NAME_1, 10L, 0.1, false, "launcherString1");
ps1.setSuspended(true, "suspendingPackage1", appExtras1, launcherExtras1, 0); ps1.setSuspended(true, "suspendingPackage1", "dialogMsg1", appExtras1, launcherExtras1, 0);
settingsUnderTest.mPackages.put(PACKAGE_NAME_1, ps1); settingsUnderTest.mPackages.put(PACKAGE_NAME_1, ps1);
ps2.setSuspended(true, "suspendingPackage2", null, null, 0); ps2.setSuspended(true, "suspendingPackage2", "dialogMsg2", null, null, 0);
settingsUnderTest.mPackages.put(PACKAGE_NAME_2, ps2); settingsUnderTest.mPackages.put(PACKAGE_NAME_2, ps2);
ps3.setSuspended(false, "irrelevant", null, null, 0); ps3.setSuspended(false, "irrelevant", "irrevelant2", null, null, 0);
settingsUnderTest.mPackages.put(PACKAGE_NAME_3, ps3); settingsUnderTest.mPackages.put(PACKAGE_NAME_3, ps3);
settingsUnderTest.writePackageRestrictionsLPr(0); settingsUnderTest.writePackageRestrictionsLPr(0);
@@ -220,6 +220,7 @@ public class PackageManagerSettingsTests {
readUserState(0); readUserState(0);
assertThat(readPus1.suspended, is(true)); assertThat(readPus1.suspended, is(true));
assertThat(readPus1.suspendingPackage, equalTo("suspendingPackage1")); assertThat(readPus1.suspendingPackage, equalTo("suspendingPackage1"));
assertThat(readPus1.dialogMessage, equalTo("dialogMsg1"));
assertThat(BaseBundle.kindofEquals(readPus1.suspendedAppExtras, appExtras1), is(true)); assertThat(BaseBundle.kindofEquals(readPus1.suspendedAppExtras, appExtras1), is(true));
assertThat(BaseBundle.kindofEquals(readPus1.suspendedLauncherExtras, launcherExtras1), assertThat(BaseBundle.kindofEquals(readPus1.suspendedLauncherExtras, launcherExtras1),
is(true)); is(true));
@@ -228,12 +229,17 @@ public class PackageManagerSettingsTests {
readUserState(0); readUserState(0);
assertThat(readPus2.suspended, is(true)); assertThat(readPus2.suspended, is(true));
assertThat(readPus2.suspendingPackage, equalTo("suspendingPackage2")); assertThat(readPus2.suspendingPackage, equalTo("suspendingPackage2"));
assertThat(readPus2.dialogMessage, equalTo("dialogMsg2"));
assertThat(readPus2.suspendedAppExtras, is(nullValue())); assertThat(readPus2.suspendedAppExtras, is(nullValue()));
assertThat(readPus2.suspendedLauncherExtras, is(nullValue())); assertThat(readPus2.suspendedLauncherExtras, is(nullValue()));
final PackageUserState readPus3 = settingsUnderTest.mPackages.get(PACKAGE_NAME_3). final PackageUserState readPus3 = settingsUnderTest.mPackages.get(PACKAGE_NAME_3).
readUserState(0); readUserState(0);
assertThat(readPus3.suspended, is(false)); assertThat(readPus3.suspended, is(false));
assertThat(readPus3.suspendingPackage, is(nullValue()));
assertThat(readPus3.dialogMessage, is(nullValue()));
assertThat(readPus3.suspendedAppExtras, is(nullValue()));
assertThat(readPus3.suspendedLauncherExtras, is(nullValue()));
} }
@Test @Test

View File

@@ -184,30 +184,40 @@ public class PackageUserStateTest {
launcherExtras2.putString("name", "launcherExtras2"); launcherExtras2.putString("name", "launcherExtras2");
final String suspendingPackage1 = "package1"; final String suspendingPackage1 = "package1";
final String suspendingPackage2 = "package2"; final String suspendingPackage2 = "package2";
final String dialogMessage1 = "dialogMessage1";
final String dialogMessage2 = "dialogMessage2";
final PackageUserState testUserState1 = new PackageUserState(); final PackageUserState testUserState1 = new PackageUserState();
testUserState1.suspended = true; testUserState1.suspended = true;
testUserState1.suspendedAppExtras = appExtras1; testUserState1.suspendedAppExtras = appExtras1;
testUserState1.suspendedLauncherExtras = launcherExtras1; testUserState1.suspendedLauncherExtras = launcherExtras1;
testUserState1.suspendingPackage = suspendingPackage1; testUserState1.suspendingPackage = suspendingPackage1;
testUserState1.dialogMessage = dialogMessage1;
final PackageUserState testUserState2 = new PackageUserState(testUserState1); PackageUserState testUserState2 = new PackageUserState(testUserState1);
assertThat(testUserState1.equals(testUserState2), is(true)); assertThat(testUserState1.equals(testUserState2), is(true));
testUserState2.suspendingPackage = suspendingPackage2; testUserState2.suspendingPackage = suspendingPackage2;
assertThat(testUserState1.equals(testUserState2), is(false)); assertThat(testUserState1.equals(testUserState2), is(false));
testUserState2.suspendingPackage = testUserState1.suspendingPackage; testUserState2 = new PackageUserState(testUserState1);
testUserState2.suspendedAppExtras = appExtras2; testUserState2.suspendedAppExtras = appExtras2;
assertThat(testUserState1.equals(testUserState2), is(false)); assertThat(testUserState1.equals(testUserState2), is(false));
testUserState2.suspendedAppExtras = testUserState1.suspendedAppExtras; testUserState2 = new PackageUserState(testUserState1);
testUserState2.suspendedLauncherExtras = launcherExtras2; testUserState2.suspendedLauncherExtras = launcherExtras2;
assertThat(testUserState1.equals(testUserState2), is(false)); assertThat(testUserState1.equals(testUserState2), is(false));
// Everything is different but irrelevant if suspended is false testUserState2 = new PackageUserState(testUserState1);
testUserState2.dialogMessage = dialogMessage2;
assertThat(testUserState1.equals(testUserState2), is(false));
testUserState2 = new PackageUserState(testUserState1);
testUserState2.suspended = testUserState1.suspended = false; testUserState2.suspended = testUserState1.suspended = false;
testUserState2.suspendedAppExtras = appExtras2; // Everything is different but irrelevant if suspended is false
testUserState2.suspendingPackage = suspendingPackage2; testUserState2.suspendingPackage = suspendingPackage2;
testUserState2.dialogMessage = dialogMessage2;
testUserState2.suspendedAppExtras = appExtras2;
testUserState2.suspendedLauncherExtras = launcherExtras2;
assertThat(testUserState1.equals(testUserState2), is(true)); assertThat(testUserState1.equals(testUserState2), is(true));
} }

View File

@@ -21,6 +21,7 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull; import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import android.app.AppGlobals; import android.app.AppGlobals;
import android.content.BroadcastReceiver; import android.content.BroadcastReceiver;
@@ -41,8 +42,15 @@ import android.os.UserHandle;
import android.support.test.InstrumentationRegistry; import android.support.test.InstrumentationRegistry;
import android.support.test.filters.LargeTest; import android.support.test.filters.LargeTest;
import android.support.test.runner.AndroidJUnit4; import android.support.test.runner.AndroidJUnit4;
import android.support.test.uiautomator.By;
import android.support.test.uiautomator.UiDevice;
import android.support.test.uiautomator.UiObject2;
import android.support.test.uiautomator.Until;
import android.util.Log; import android.util.Log;
import android.view.IWindowManager;
import android.view.WindowManagerGlobal;
import com.android.servicestests.apps.suspendtestapp.SuspendTestActivity;
import com.android.servicestests.apps.suspendtestapp.SuspendTestReceiver; import com.android.servicestests.apps.suspendtestapp.SuspendTestReceiver;
import org.junit.After; import org.junit.After;
@@ -68,14 +76,25 @@ public class SuspendPackagesTest {
INSTRUMENTATION_PACKAGE + ".action.REPORT_MY_PACKAGE_SUSPENDED"; INSTRUMENTATION_PACKAGE + ".action.REPORT_MY_PACKAGE_SUSPENDED";
public static final String ACTION_REPORT_MY_PACKAGE_UNSUSPENDED = public static final String ACTION_REPORT_MY_PACKAGE_UNSUSPENDED =
INSTRUMENTATION_PACKAGE + ".action.REPORT_MY_PACKAGE_UNSUSPENDED"; INSTRUMENTATION_PACKAGE + ".action.REPORT_MY_PACKAGE_UNSUSPENDED";
public static final String ACTION_REPORT_TEST_ACTIVITY_STARTED =
INSTRUMENTATION_PACKAGE + ".action.REPORT_TEST_ACTIVITY_STARTED";
public static final String ACTION_REPORT_TEST_ACTIVITY_STOPPED =
INSTRUMENTATION_PACKAGE + ".action.REPORT_TEST_ACTIVITY_STOPPED";
public static final String ACTION_REPORT_MORE_DETAILS_ACTIVITY_STARTED =
INSTRUMENTATION_PACKAGE + ".action.REPORT_MORE_DETAILS_ACTIVITY_STARTED";
public static final String ACTION_FINISH_TEST_ACTIVITY =
INSTRUMENTATION_PACKAGE + ".action.FINISH_TEST_ACTIVITY";
public static final String EXTRA_RECEIVED_PACKAGE_NAME =
SuspendPackagesTest.INSTRUMENTATION_PACKAGE + ".extra.RECEIVED_PACKAGE_NAME";
private Context mContext; private Context mContext;
private PackageManager mPackageManager; private PackageManager mPackageManager;
private LauncherApps mLauncherApps; private LauncherApps mLauncherApps;
private Handler mReceiverHandler; private Handler mReceiverHandler;
private ComponentName mTestReceiverComponent;
private AppCommunicationReceiver mAppCommsReceiver; private AppCommunicationReceiver mAppCommsReceiver;
private StubbedCallback mTestCallback; private StubbedCallback mTestCallback;
private UiDevice mUiDevice;
private static final class AppCommunicationReceiver extends BroadcastReceiver { private static final class AppCommunicationReceiver extends BroadcastReceiver {
private Context context; private Context context;
@@ -86,11 +105,12 @@ public class SuspendPackagesTest {
this.context = context; this.context = context;
} }
void register(Handler handler) { void register(Handler handler, String... actions) {
registered = true; registered = true;
final IntentFilter intentFilter = new IntentFilter(); final IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(ACTION_REPORT_MY_PACKAGE_SUSPENDED); for (String action : actions) {
intentFilter.addAction(ACTION_REPORT_MY_PACKAGE_UNSUSPENDED); intentFilter.addAction(action);
}
context.registerReceiver(this, intentFilter, null, handler); context.registerReceiver(this, intentFilter, null, handler);
} }
@@ -110,19 +130,28 @@ public class SuspendPackagesTest {
} }
} }
Intent receiveIntentFromApp() { Intent pollForIntent(long secondsToWait) {
if (!registered) { if (!registered) {
throw new IllegalStateException("Receiver not registered"); throw new IllegalStateException("Receiver not registered");
} }
final Intent intent; final Intent intent;
try { try {
intent = intentQueue.poll(5, TimeUnit.SECONDS); intent = intentQueue.poll(secondsToWait, TimeUnit.SECONDS);
} catch (InterruptedException ie) { } catch (InterruptedException ie) {
throw new RuntimeException("Interrupted while waiting for app broadcast", ie); throw new RuntimeException("Interrupted while waiting for app broadcast", ie);
} }
assertNotNull("No intent received from app within 5 seconds", intent);
return intent; return intent;
} }
void drainPendingBroadcasts() {
while (pollForIntent(5) != null);
}
Intent receiveIntentFromApp() {
final Intent intentReceived = pollForIntent(5);
assertNotNull("No intent received from app within 5 seconds", intentReceived);
return intentReceived;
}
} }
@Before @Before
@@ -131,8 +160,7 @@ public class SuspendPackagesTest {
mPackageManager = mContext.getPackageManager(); mPackageManager = mContext.getPackageManager();
mLauncherApps = (LauncherApps) mContext.getSystemService(Context.LAUNCHER_APPS_SERVICE); mLauncherApps = (LauncherApps) mContext.getSystemService(Context.LAUNCHER_APPS_SERVICE);
mReceiverHandler = new Handler(Looper.getMainLooper()); mReceiverHandler = new Handler(Looper.getMainLooper());
mTestReceiverComponent = new ComponentName(TEST_APP_PACKAGE_NAME, mUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
SuspendTestReceiver.class.getCanonicalName());
IPackageManager ipm = AppGlobals.getPackageManager(); IPackageManager ipm = AppGlobals.getPackageManager();
try { try {
// Otherwise implicit broadcasts will not be delivered. // Otherwise implicit broadcasts will not be delivered.
@@ -151,9 +179,10 @@ public class SuspendPackagesTest {
private Bundle requestAppAction(String action) throws InterruptedException { private Bundle requestAppAction(String action) throws InterruptedException {
final AtomicReference<Bundle> result = new AtomicReference<>(); final AtomicReference<Bundle> result = new AtomicReference<>();
final CountDownLatch receiverLatch = new CountDownLatch(1); final CountDownLatch receiverLatch = new CountDownLatch(1);
final ComponentName testReceiverComponent = new ComponentName(TEST_APP_PACKAGE_NAME,
SuspendTestReceiver.class.getCanonicalName());
final Intent broadcastIntent = new Intent(action) final Intent broadcastIntent = new Intent(action)
.setComponent(mTestReceiverComponent) .setComponent(testReceiverComponent)
.setFlags(Intent.FLAG_RECEIVER_FOREGROUND); .setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
mContext.sendOrderedBroadcast(broadcastIntent, null, new BroadcastReceiver() { mContext.sendOrderedBroadcast(broadcastIntent, null, new BroadcastReceiver() {
@Override @Override
@@ -175,9 +204,10 @@ public class SuspendPackagesTest {
return extras; return extras;
} }
private void suspendTestPackage(PersistableBundle appExtras, PersistableBundle launcherExtras) { private void suspendTestPackage(PersistableBundle appExtras, PersistableBundle launcherExtras,
String dialogMessage) {
final String[] unchangedPackages = mPackageManager.setPackagesSuspended( final String[] unchangedPackages = mPackageManager.setPackagesSuspended(
PACKAGES_TO_SUSPEND, true, appExtras, launcherExtras, null); PACKAGES_TO_SUSPEND, true, appExtras, launcherExtras, dialogMessage);
assertTrue("setPackagesSuspended returned non-empty list", unchangedPackages.length == 0); assertTrue("setPackagesSuspended returned non-empty list", unchangedPackages.length == 0);
} }
@@ -187,6 +217,13 @@ public class SuspendPackagesTest {
assertTrue("setPackagesSuspended returned non-empty list", unchangedPackages.length == 0); assertTrue("setPackagesSuspended returned non-empty list", unchangedPackages.length == 0);
} }
private void startTestAppActivity() {
final Intent testActivity = new Intent()
.setComponent(new ComponentName(TEST_APP_PACKAGE_NAME,
SuspendTestActivity.class.getCanonicalName()))
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mContext.startActivity(testActivity);
}
private static boolean areSameExtras(BaseBundle expected, BaseBundle received) { private static boolean areSameExtras(BaseBundle expected, BaseBundle received) {
if (expected != null) { if (expected != null) {
@@ -199,13 +236,14 @@ public class SuspendPackagesTest {
} }
private static void assertSameExtras(String message, BaseBundle expected, BaseBundle received) { private static void assertSameExtras(String message, BaseBundle expected, BaseBundle received) {
assertTrue(message + ": [expected: " + expected + "; received: " + received + "]", if (!areSameExtras(expected, received)) {
areSameExtras(expected, received)); fail(message + ": [expected: " + expected + "; received: " + received + "]");
}
} }
@Test @Test
public void testIsPackageSuspended() { public void testIsPackageSuspended() {
suspendTestPackage(null, null); suspendTestPackage(null, null, null);
assertTrue("isPackageSuspended is false", assertTrue("isPackageSuspended is false",
mPackageManager.isPackageSuspended(TEST_APP_PACKAGE_NAME)); mPackageManager.isPackageSuspended(TEST_APP_PACKAGE_NAME));
} }
@@ -217,7 +255,7 @@ public class SuspendPackagesTest {
assertNull(resultFromApp.getBundle(SuspendTestReceiver.EXTRA_SUSPENDED_APP_EXTRAS)); assertNull(resultFromApp.getBundle(SuspendTestReceiver.EXTRA_SUSPENDED_APP_EXTRAS));
final PersistableBundle appExtras = getExtras("testSuspendedStateFromApp", 20, "20", 0.2); final PersistableBundle appExtras = getExtras("testSuspendedStateFromApp", 20, "20", 0.2);
suspendTestPackage(appExtras, null); suspendTestPackage(appExtras, null, null);
resultFromApp = requestAppAction(SuspendTestReceiver.ACTION_GET_SUSPENDED_STATE); resultFromApp = requestAppAction(SuspendTestReceiver.ACTION_GET_SUSPENDED_STATE);
assertTrue("resultFromApp:suspended is false", assertTrue("resultFromApp:suspended is false",
@@ -230,37 +268,39 @@ public class SuspendPackagesTest {
@Test @Test
public void testMyPackageSuspendedUnsuspended() { public void testMyPackageSuspendedUnsuspended() {
mAppCommsReceiver.register(mReceiverHandler); mAppCommsReceiver.register(mReceiverHandler, ACTION_REPORT_MY_PACKAGE_SUSPENDED,
ACTION_REPORT_MY_PACKAGE_UNSUSPENDED);
mAppCommsReceiver.drainPendingBroadcasts();
final PersistableBundle appExtras = getExtras("testMyPackageSuspendBroadcasts", 1, "1", .1); final PersistableBundle appExtras = getExtras("testMyPackageSuspendBroadcasts", 1, "1", .1);
suspendTestPackage(appExtras, null); suspendTestPackage(appExtras, null, null);
Intent intentFromApp = mAppCommsReceiver.receiveIntentFromApp(); Intent intentFromApp = mAppCommsReceiver.receiveIntentFromApp();
assertTrue("MY_PACKAGE_SUSPENDED delivery not reported", assertEquals("MY_PACKAGE_SUSPENDED delivery not reported",
ACTION_REPORT_MY_PACKAGE_SUSPENDED.equals(intentFromApp.getAction())); ACTION_REPORT_MY_PACKAGE_SUSPENDED, intentFromApp.getAction());
assertSameExtras("Received app extras different to the ones supplied", appExtras, assertSameExtras("Received app extras different to the ones supplied", appExtras,
intentFromApp.getBundleExtra(SuspendTestReceiver.EXTRA_SUSPENDED_APP_EXTRAS)); intentFromApp.getBundleExtra(SuspendTestReceiver.EXTRA_SUSPENDED_APP_EXTRAS));
unsuspendTestPackage(); unsuspendTestPackage();
intentFromApp = mAppCommsReceiver.receiveIntentFromApp(); intentFromApp = mAppCommsReceiver.receiveIntentFromApp();
assertTrue("MY_PACKAGE_UNSUSPENDED delivery not reported", assertEquals("MY_PACKAGE_UNSUSPENDED delivery not reported",
ACTION_REPORT_MY_PACKAGE_UNSUSPENDED.equals(intentFromApp.getAction())); ACTION_REPORT_MY_PACKAGE_UNSUSPENDED, intentFromApp.getAction());
} }
@Test @Test
public void testUpdatingAppExtras() { public void testUpdatingAppExtras() {
mAppCommsReceiver.register(mReceiverHandler); mAppCommsReceiver.register(mReceiverHandler, ACTION_REPORT_MY_PACKAGE_SUSPENDED);
final PersistableBundle extras1 = getExtras("testMyPackageSuspendedOnChangingExtras", 1, final PersistableBundle extras1 = getExtras("testMyPackageSuspendedOnChangingExtras", 1,
"1", 0.1); "1", 0.1);
suspendTestPackage(extras1, null); suspendTestPackage(extras1, null, null);
Intent intentFromApp = mAppCommsReceiver.receiveIntentFromApp(); Intent intentFromApp = mAppCommsReceiver.receiveIntentFromApp();
assertTrue("MY_PACKAGE_SUSPENDED delivery not reported", assertEquals("MY_PACKAGE_SUSPENDED delivery not reported",
ACTION_REPORT_MY_PACKAGE_SUSPENDED.equals(intentFromApp.getAction())); ACTION_REPORT_MY_PACKAGE_SUSPENDED, intentFromApp.getAction());
assertSameExtras("Received app extras different to the ones supplied", extras1, assertSameExtras("Received app extras different to the ones supplied", extras1,
intentFromApp.getBundleExtra(SuspendTestReceiver.EXTRA_SUSPENDED_APP_EXTRAS)); intentFromApp.getBundleExtra(SuspendTestReceiver.EXTRA_SUSPENDED_APP_EXTRAS));
final PersistableBundle extras2 = getExtras("testMyPackageSuspendedOnChangingExtras", 2, final PersistableBundle extras2 = getExtras("testMyPackageSuspendedOnChangingExtras", 2,
"2", 0.2); "2", 0.2);
mPackageManager.setSuspendedPackageAppExtras(TEST_APP_PACKAGE_NAME, extras2); mPackageManager.setSuspendedPackageAppExtras(TEST_APP_PACKAGE_NAME, extras2);
intentFromApp = mAppCommsReceiver.receiveIntentFromApp(); intentFromApp = mAppCommsReceiver.receiveIntentFromApp();
assertTrue("MY_PACKAGE_SUSPENDED delivery not reported", assertEquals("MY_PACKAGE_SUSPENDED delivery not reported",
ACTION_REPORT_MY_PACKAGE_SUSPENDED.equals(intentFromApp.getAction())); ACTION_REPORT_MY_PACKAGE_SUSPENDED, intentFromApp.getAction());
assertSameExtras("Received app extras different to the updated extras", extras2, assertSameExtras("Received app extras different to the updated extras", extras2,
intentFromApp.getBundleExtra(SuspendTestReceiver.EXTRA_SUSPENDED_APP_EXTRAS)); intentFromApp.getBundleExtra(SuspendTestReceiver.EXTRA_SUSPENDED_APP_EXTRAS));
} }
@@ -273,13 +313,27 @@ public class SuspendPackagesTest {
assertEquals(mContext.getOpPackageName(), unchangedPkgs[0]); assertEquals(mContext.getOpPackageName(), unchangedPkgs[0]);
} }
@Test
public void testActivityStoppedOnSuspend() {
mAppCommsReceiver.register(mReceiverHandler, ACTION_REPORT_TEST_ACTIVITY_STARTED,
ACTION_REPORT_TEST_ACTIVITY_STOPPED);
startTestAppActivity();
Intent intentFromApp = mAppCommsReceiver.receiveIntentFromApp();
assertEquals("Test activity start not reported",
ACTION_REPORT_TEST_ACTIVITY_STARTED, intentFromApp.getAction());
suspendTestPackage(null, null, null);
intentFromApp = mAppCommsReceiver.receiveIntentFromApp();
assertEquals("Test activity stop not reported on suspending the test app",
ACTION_REPORT_TEST_ACTIVITY_STOPPED, intentFromApp.getAction());
}
@Test @Test
public void testGetLauncherExtrasNonNull() { public void testGetLauncherExtrasNonNull() {
final Bundle extrasWhenUnsuspended = mLauncherApps.getSuspendedPackageLauncherExtras( final Bundle extrasWhenUnsuspended = mLauncherApps.getSuspendedPackageLauncherExtras(
TEST_APP_PACKAGE_NAME, mContext.getUser()); TEST_APP_PACKAGE_NAME, mContext.getUser());
assertNull("Non null extras when package unsuspended:", extrasWhenUnsuspended); assertNull("Non null extras when package unsuspended:", extrasWhenUnsuspended);
final PersistableBundle launcherExtras = getExtras("testGetLauncherExtras", 1, "1", 0.1); final PersistableBundle launcherExtras = getExtras("testGetLauncherExtras", 1, "1", 0.1);
suspendTestPackage(null, launcherExtras); suspendTestPackage(null, launcherExtras, null);
final Bundle receivedExtras = mLauncherApps.getSuspendedPackageLauncherExtras( final Bundle receivedExtras = mLauncherApps.getSuspendedPackageLauncherExtras(
TEST_APP_PACKAGE_NAME, mContext.getUser()); TEST_APP_PACKAGE_NAME, mContext.getUser());
assertSameExtras("Received launcher extras different to the ones supplied", launcherExtras, assertSameExtras("Received launcher extras different to the ones supplied", launcherExtras,
@@ -288,7 +342,7 @@ public class SuspendPackagesTest {
@Test @Test
public void testGetLauncherExtrasNull() { public void testGetLauncherExtrasNull() {
suspendTestPackage(null, null); suspendTestPackage(null, null, null);
final Bundle extrasWhenNoneGiven = mLauncherApps.getSuspendedPackageLauncherExtras( final Bundle extrasWhenNoneGiven = mLauncherApps.getSuspendedPackageLauncherExtras(
TEST_APP_PACKAGE_NAME, mContext.getUser()); TEST_APP_PACKAGE_NAME, mContext.getUser());
assertNull("Non null extras when null extras provided:", extrasWhenNoneGiven); assertNull("Non null extras when null extras provided:", extrasWhenNoneGiven);
@@ -339,7 +393,7 @@ public class SuspendPackagesTest {
} }
}; };
mLauncherApps.registerCallback(mTestCallback, mReceiverHandler); mLauncherApps.registerCallback(mTestCallback, mReceiverHandler);
suspendTestPackage(null, suppliedExtras); suspendTestPackage(null, suppliedExtras, null);
assertFalse("Both callbacks were invoked", twoCallbackLatch.await(5, TimeUnit.SECONDS)); assertFalse("Both callbacks were invoked", twoCallbackLatch.await(5, TimeUnit.SECONDS));
twoCallbackLatch.countDown(); twoCallbackLatch.countDown();
assertTrue("No callback was invoked", twoCallbackLatch.await(2, TimeUnit.SECONDS)); assertTrue("No callback was invoked", twoCallbackLatch.await(2, TimeUnit.SECONDS));
@@ -373,19 +427,53 @@ public class SuspendPackagesTest {
} }
}; };
mLauncherApps.registerCallback(mTestCallback, mReceiverHandler); mLauncherApps.registerCallback(mTestCallback, mReceiverHandler);
suspendTestPackage(null, suppliedExtras); suspendTestPackage(null, suppliedExtras, null);
assertTrue("Callback not invoked", oneCallbackLatch.await(5, TimeUnit.SECONDS)); assertTrue("Callback not invoked", oneCallbackLatch.await(5, TimeUnit.SECONDS));
final String result = overridingOneCallbackResult.get(); final String result = overridingOneCallbackResult.get();
assertTrue("Callback did not complete as expected: " + result, result.isEmpty()); assertTrue("Callback did not complete as expected: " + result, result.isEmpty());
} }
private void turnScreenOn() throws Exception {
if (!mUiDevice.isScreenOn()) {
mUiDevice.wakeUp();
}
final IWindowManager wm = WindowManagerGlobal.getWindowManagerService();
wm.dismissKeyguard(null, null);
}
@Test
public void testInterceptorActivity() throws Exception {
turnScreenOn();
mAppCommsReceiver.register(mReceiverHandler, ACTION_REPORT_MORE_DETAILS_ACTIVITY_STARTED,
ACTION_REPORT_TEST_ACTIVITY_STARTED);
final String testMessage = "This is a test message";
suspendTestPackage(null, null, testMessage);
startTestAppActivity();
assertNull("No broadcast was expected from app", mAppCommsReceiver.pollForIntent(2));
assertNotNull("Given dialog message not shown",
mUiDevice.wait(Until.findObject(By.text(testMessage)), 5000));
final String buttonText = "More details";
final UiObject2 moreDetailsButton = mUiDevice.findObject(
By.clickable(true).text(buttonText));
assertNotNull("\"More Details\" button not shown", moreDetailsButton);
moreDetailsButton.click();
final Intent intentFromApp = mAppCommsReceiver.receiveIntentFromApp();
assertEquals("\"More Details\" activity start not reported",
ACTION_REPORT_MORE_DETAILS_ACTIVITY_STARTED, intentFromApp.getAction());
final String receivedPackageName = intentFromApp.getStringExtra(
EXTRA_RECEIVED_PACKAGE_NAME);
assertEquals("Wrong package name received by \"More Details\" activity",
TEST_APP_PACKAGE_NAME, receivedPackageName);
}
@After @After
public void tearDown() throws Exception { public void tearDown() {
mAppCommsReceiver.unregister(); mAppCommsReceiver.unregister();
if (mTestCallback != null) { if (mTestCallback != null) {
mLauncherApps.unregisterCallback(mTestCallback); mLauncherApps.unregisterCallback(mTestCallback);
} }
Thread.sleep(250); // To prevent any race with the next registerReceiver mContext.sendBroadcast(new Intent(ACTION_FINISH_TEST_ACTIVITY)
.setPackage(TEST_APP_PACKAGE_NAME));
} }
private static abstract class StubbedCallback extends LauncherApps.Callback { private static abstract class StubbedCallback extends LauncherApps.Callback {

View File

@@ -0,0 +1,42 @@
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.pm;
import static com.android.server.pm.SuspendPackagesTest.ACTION_REPORT_MORE_DETAILS_ACTIVITY_STARTED;
import static com.android.server.pm.SuspendPackagesTest.EXTRA_RECEIVED_PACKAGE_NAME;
import static com.android.server.pm.SuspendPackagesTest.INSTRUMENTATION_PACKAGE;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
public class SuspendedDetailsActivity extends Activity {
private static final String TAG = SuspendedDetailsActivity.class.getSimpleName();
@Override
protected void onStart() {
Log.d(TAG, "onStart");
final String suspendedPackage = getIntent().getStringExtra(Intent.EXTRA_PACKAGE_NAME);
super.onStart();
final Intent reportStart = new Intent(ACTION_REPORT_MORE_DETAILS_ACTIVITY_STARTED)
.putExtra(EXTRA_RECEIVED_PACKAGE_NAME, suspendedPackage)
.setPackage(INSTRUMENTATION_PACKAGE);
sendBroadcast(reportStart);
finish();
}
}

View File

@@ -20,7 +20,7 @@ LOCAL_MODULE_TAGS := tests
LOCAL_COMPATIBILITY_SUITE := device-tests LOCAL_COMPATIBILITY_SUITE := device-tests
LOCAL_STATIC_JAVA_LIBRARIES := android-support-test LOCAL_STATIC_JAVA_LIBRARIES := android-support-test ub-uiautomator
LOCAL_SRC_FILES := $(call all-subdir-java-files) LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_SRC_FILES += ../../src/com/android/server/pm/SuspendPackagesTest.java LOCAL_SRC_FILES += ../../src/com/android/server/pm/SuspendPackagesTest.java

View File

@@ -16,9 +16,45 @@
package com.android.servicestests.apps.suspendtestapp; package com.android.servicestests.apps.suspendtestapp;
import static com.android.server.pm.SuspendPackagesTest.ACTION_FINISH_TEST_ACTIVITY;
import static com.android.server.pm.SuspendPackagesTest.ACTION_REPORT_TEST_ACTIVITY_STARTED;
import static com.android.server.pm.SuspendPackagesTest.ACTION_REPORT_TEST_ACTIVITY_STOPPED;
import static com.android.server.pm.SuspendPackagesTest.INSTRUMENTATION_PACKAGE;
import android.app.Activity; import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.util.Log;
public class SuspendTestActivity extends Activity { public class SuspendTestActivity extends Activity {
private static final String TAG = SuspendTestActivity.class.getSimpleName(); private static final String TAG = SuspendTestActivity.class.getSimpleName();
} private BroadcastReceiver mFinishReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG, "Finishing test activity from receiver");
SuspendTestActivity.this.finish();
}
};
@Override
public void onStart() {
Log.d(TAG, "onStart");
final Intent reportStart = new Intent(ACTION_REPORT_TEST_ACTIVITY_STARTED)
.setPackage(INSTRUMENTATION_PACKAGE);
sendBroadcast(reportStart);
registerReceiver(mFinishReceiver, new IntentFilter(ACTION_FINISH_TEST_ACTIVITY));
super.onStart();
}
@Override
public void onStop() {
Log.d(TAG, "onStop");
final Intent reportStop = new Intent(ACTION_REPORT_TEST_ACTIVITY_STOPPED)
.setPackage(INSTRUMENTATION_PACKAGE);
sendBroadcast(reportStop);
unregisterReceiver(mFinishReceiver);
super.onStop();
}
}