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 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_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 SERIAL_PORT = "android.permission.SERIAL_PORT";
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_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_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 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";

View File

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

View File

@@ -2281,6 +2281,28 @@ public class Intent implements Parcelable, Cloneable {
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
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.
*

View File

@@ -273,8 +273,8 @@ interface IPackageManager {
void clearCrossProfileIntentFilters(int sourceUserId, String ownerPackage);
String[] setPackagesSuspendedAsUser(in String[] packageNames, boolean suspended,
in PersistableBundle launcherExtras, in PersistableBundle appExtras,
String callingPackage, int userId);
in PersistableBundle appExtras, in PersistableBundle launcherExtras,
String dialogMessage, String callingPackage, 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
* {@link PackageManager#setPackagesSuspended(String[], boolean, PersistableBundle,
* PersistableBundle, String)}
* PersistableBundle, String)}.
*
* @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.
*
* @see PackageManager#setPackagesSuspended(String[], boolean, PersistableBundle,
@@ -213,6 +213,29 @@ public abstract class PackageManagerInternal {
*/
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.
* @see PackageManager#getPackageUidAsUser(String, int, int)

View File

@@ -34,6 +34,7 @@ import android.util.ArraySet;
import com.android.internal.util.ArrayUtils;
import java.util.Arrays;
import java.util.Objects;
/**
* 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 suspended;
public String suspendingPackage;
public String dialogMessage; // Message to show when a suspended package launch attempt is made
public PersistableBundle suspendedAppExtras;
public PersistableBundle suspendedLauncherExtras;
public boolean instantApp;
@@ -82,6 +84,7 @@ public class PackageUserState {
hidden = o.hidden;
suspended = o.suspended;
suspendingPackage = o.suspendingPackage;
dialogMessage = o.dialogMessage;
suspendedAppExtras = o.suspendedAppExtras;
suspendedLauncherExtras = o.suspendedLauncherExtras;
instantApp = o.instantApp;
@@ -208,6 +211,9 @@ public class PackageUserState {
|| !suspendingPackage.equals(oldState.suspendingPackage)) {
return false;
}
if (!Objects.equals(dialogMessage, oldState.dialogMessage)) {
return false;
}
if (!BaseBundle.kindofEquals(suspendedAppExtras,
oldState.suspendedAppExtras)) {
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"
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}
API is no longer supported. -->
<permission android:name="android.permission.RESTART_PACKAGES"
@@ -4116,6 +4125,12 @@
</intent-filter>
</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"
android:theme="@style/Theme.DeviceDefault.Light.Dialog.Alert"
android:excludeFromRecents="true"

View File

@@ -4703,6 +4703,13 @@
<!-- Menu item in the locale menu [CHAR LIMIT=30] -->
<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] -->
<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] -->

View File

@@ -2877,6 +2877,10 @@
<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 -->
<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.pm.ApplicationInfo.FLAG_SUSPENDED;
import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
import android.Manifest;
import android.app.ActivityOptions;
import android.app.AppGlobals;
import android.app.KeyguardManager;
@@ -41,6 +44,7 @@ import android.content.IIntentSender;
import android.content.Intent;
import android.content.IntentSender;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManagerInternal;
import android.content.pm.ResolveInfo;
import android.content.pm.UserInfo;
import android.os.Binder;
@@ -51,6 +55,7 @@ import android.os.UserManager;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.HarmfulAppWarningActivity;
import com.android.internal.app.SuspendedAppActivity;
import com.android.internal.app.UnlaunchableAppActivity;
import com.android.server.LocalServices;
@@ -151,7 +156,7 @@ class ActivityStartInterceptor {
mInTask = inTask;
mActivityOptions = activityOptions;
if (interceptSuspendPackageIfNeed()) {
if (interceptSuspendedPackageIfNeeded()) {
// Skip the rest of interceptions as the package is suspended by device admin so
// no user action can undo this.
return true;
@@ -204,12 +209,7 @@ class ActivityStartInterceptor {
return true;
}
private boolean interceptSuspendPackageIfNeed() {
// 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;
}
private boolean interceptSuspendedByAdminPackage() {
DevicePolicyManagerInternal devicePolicyManager = LocalServices
.getService(DevicePolicyManagerInternal.class);
if (devicePolicyManager == null) {
@@ -232,6 +232,55 @@ class ActivityStartInterceptor {
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() {
final Intent interceptingIntent = interceptWithConfirmCredentialsIfNeeded(mAInfo, mUserId);
if (interceptingIntent == null) {

View File

@@ -274,6 +274,7 @@ import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.app.IMediaContainerService;
import com.android.internal.app.ResolverActivity;
import com.android.internal.app.SuspendedAppActivity;
import com.android.internal.content.NativeLibraryHelper;
import com.android.internal.content.PackageHelper;
import com.android.internal.logging.MetricsLogger;
@@ -14006,8 +14007,8 @@ public class PackageManagerService extends IPackageManager.Stub
@Override
public String[] setPackagesSuspendedAsUser(String[] packageNames, boolean suspended,
PersistableBundle appExtras, PersistableBundle launcherExtras, String callingPackage,
int userId) {
PersistableBundle appExtras, PersistableBundle launcherExtras, String dialogMessage,
String callingPackage, int userId) {
try {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.SUSPEND_APPS, null);
} catch (SecurityException e) {
@@ -14055,7 +14056,7 @@ public class PackageManagerService extends IPackageManager.Stub
unactionedPackages.add(packageName);
continue;
}
pkgSetting.setSuspended(suspended, callingPackage, appExtras,
pkgSetting.setSuspended(suspended, callingPackage, dialogMessage, appExtras,
launcherExtras, userId);
changedPackagesList.add(packageName);
}
@@ -14177,7 +14178,7 @@ public class PackageManagerService extends IPackageManager.Stub
for (int user : userIds) {
final PackageUserState pus = ps.readUserState(user);
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 /*suspended*/,
null, /*suspendingPackage*/
null, /*dialogMessage*/
null, /*suspendedAppExtras*/
null, /*suspendedLauncherExtras*/
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
public int getPackageUid(String packageName, int flags, int userId) {
return PackageManagerService.this

View File

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

View File

@@ -398,11 +398,12 @@ public abstract class PackageSettingBase extends SettingBase {
return readUserState(userId).suspended;
}
void setSuspended(boolean suspended, String suspendingPackage, PersistableBundle appExtras,
PersistableBundle launcherExtras, int userId) {
void setSuspended(boolean suspended, String suspendingPackage, String dialogMessage,
PersistableBundle appExtras, PersistableBundle launcherExtras, int userId) {
final PackageUserState existingUserState = modifyUserState(userId);
existingUserState.suspended = suspended;
existingUserState.suspendingPackage = suspended ? suspendingPackage : null;
existingUserState.dialogMessage = suspended ? dialogMessage : null;
existingUserState.suspendedAppExtras = suspended ? appExtras : 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,
boolean notLaunched, boolean hidden, boolean suspended, String suspendingPackage,
PersistableBundle suspendedAppExtras, PersistableBundle suspendedLauncherExtras,
boolean instantApp,
String dialogMessage, PersistableBundle suspendedAppExtras,
PersistableBundle suspendedLauncherExtras, boolean instantApp,
boolean virtualPreload, String lastDisableAppCaller,
ArraySet<String> enabledComponents, ArraySet<String> disabledComponents,
int domainVerifState, int linkGeneration, int installReason,
@@ -440,6 +441,7 @@ public abstract class PackageSettingBase extends SettingBase {
state.hidden = hidden;
state.suspended = suspended;
state.suspendingPackage = suspendingPackage;
state.dialogMessage = dialogMessage;
state.suspendedAppExtras = suspendedAppExtras;
state.suspendedLauncherExtras = suspendedLauncherExtras;
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_SUSPENDED = "suspended";
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.
@Deprecated
private static final String ATTR_BLOCK_UNINSTALL = "blockUninstall";
@@ -734,6 +735,7 @@ public final class Settings {
false /*hidden*/,
false /*suspended*/,
null, /*suspendingPackage*/
null, /*dialogMessage*/
null, /*suspendedAppExtras*/
null, /*suspendedLauncherExtras*/
instantApp,
@@ -1628,6 +1630,7 @@ public final class Settings {
false /*hidden*/,
false /*suspended*/,
null, /*suspendingPackage*/
null, /*dialogMessage*/
null, /*suspendedAppExtras*/
null, /*suspendedLauncherExtras*/
false /*instantApp*/,
@@ -1704,6 +1707,8 @@ public final class Settings {
false);
String suspendingPackage = parser.getAttributeValue(null,
ATTR_SUSPENDING_PACKAGE);
final String dialogMessage = parser.getAttributeValue(null,
ATTR_SUSPEND_DIALOG_MESSAGE);
if (suspended && suspendingPackage == null) {
suspendingPackage = PLATFORM_PACKAGE_NAME;
}
@@ -1767,7 +1772,7 @@ public final class Settings {
setBlockUninstallLPw(userId, name, true);
}
ps.setUserState(userId, ceDataInode, enabled, installed, stopped, notLaunched,
hidden, suspended, suspendingPackage, suspendedAppExtras,
hidden, suspended, suspendingPackage, dialogMessage, suspendedAppExtras,
suspendedLauncherExtras, instantApp, virtualPreload, enabledCaller,
enabledComponents, disabledComponents, verifState, linkGeneration,
installReason, harmfulAppWarning);
@@ -2077,7 +2082,14 @@ public final class Settings {
}
if (ustate.suspended) {
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) {
serializer.startTag(null, TAG_SUSPENDED_APP_EXTRAS);
try {
@@ -4750,8 +4762,11 @@ public final class Settings {
pw.print(" suspended=");
pw.print(ps.getSuspended(user.id));
if (ps.getSuspended(user.id)) {
final PackageUserState pus = ps.readUserState(user.id);
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(ps.getStopped(user.id));

View File

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

View File

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

View File

@@ -63,6 +63,7 @@
<uses-permission android:name="android.permission.WATCH_APPOPS" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<uses-permission android:name="android.permission.SUSPEND_APPS"/>
<uses-permission android:name="android.permission.CONTROL_KEYGUARD"/>
<!-- Uses API introduced in O (26) -->
<uses-sdk android:minSdkVersion="1"
@@ -145,6 +146,15 @@
<activity android:name="com.android.server.pm.ShortcutTestActivity"
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-alias android:name="a.ShortcutEnabled"

View File

@@ -17,6 +17,9 @@
package com.android.server.am;
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.assertFalse;
import static org.junit.Assert.assertTrue;
@@ -30,6 +33,7 @@ import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManagerInternal;
import android.content.pm.UserInfo;
import android.os.UserHandle;
import android.os.UserManager;
@@ -79,6 +83,8 @@ public class ActivityStartInterceptorTest {
@Mock
private DevicePolicyManagerInternal mDevicePolicyManager;
@Mock
private PackageManagerInternal mPackageManagerInternal;
@Mock
private UserManager mUserManager;
@Mock
private UserController mUserController;
@@ -103,6 +109,7 @@ public class ActivityStartInterceptorTest {
when(mDevicePolicyManager
.createShowAdminSupportIntent(TEST_USER_ID, true))
.thenReturn(ADMIN_SUPPORT_INTENT);
when(mService.getPackageManagerInternalLocked()).thenReturn(mPackageManagerInternal);
// Mock UserManager
when(mContext.getSystemService(Context.USER_SERVICE)).thenReturn(mUserManager);
@@ -115,15 +122,18 @@ public class ActivityStartInterceptorTest {
thenReturn(CONFIRM_CREDENTIALS_INTENT);
// Initialise activity info
mAInfo.packageName = TEST_PACKAGE_NAME;
mAInfo.applicationInfo = new ApplicationInfo();
mAInfo.packageName = mAInfo.applicationInfo.packageName = TEST_PACKAGE_NAME;
}
@Test
public void testSuspendedPackage() {
public void testSuspendedByAdminPackage() {
// GIVEN the package we're about to launch is currently suspended
mAInfo.applicationInfo.flags = FLAG_SUSPENDED;
when(mPackageManagerInternal.getSuspendingPackage(TEST_PACKAGE_NAME, TEST_USER_ID))
.thenReturn(PLATFORM_PACKAGE_NAME);
// THEN calling intercept returns true
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");
final PersistableBundle launcherExtras1 = getPersistableBundle(
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);
ps2.setSuspended(true, "suspendingPackage2", null, null, 0);
ps2.setSuspended(true, "suspendingPackage2", "dialogMsg2", null, null, 0);
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.writePackageRestrictionsLPr(0);
@@ -220,6 +220,7 @@ public class PackageManagerSettingsTests {
readUserState(0);
assertThat(readPus1.suspended, is(true));
assertThat(readPus1.suspendingPackage, equalTo("suspendingPackage1"));
assertThat(readPus1.dialogMessage, equalTo("dialogMsg1"));
assertThat(BaseBundle.kindofEquals(readPus1.suspendedAppExtras, appExtras1), is(true));
assertThat(BaseBundle.kindofEquals(readPus1.suspendedLauncherExtras, launcherExtras1),
is(true));
@@ -228,12 +229,17 @@ public class PackageManagerSettingsTests {
readUserState(0);
assertThat(readPus2.suspended, is(true));
assertThat(readPus2.suspendingPackage, equalTo("suspendingPackage2"));
assertThat(readPus2.dialogMessage, equalTo("dialogMsg2"));
assertThat(readPus2.suspendedAppExtras, is(nullValue()));
assertThat(readPus2.suspendedLauncherExtras, is(nullValue()));
final PackageUserState readPus3 = settingsUnderTest.mPackages.get(PACKAGE_NAME_3).
readUserState(0);
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

View File

@@ -184,30 +184,40 @@ public class PackageUserStateTest {
launcherExtras2.putString("name", "launcherExtras2");
final String suspendingPackage1 = "package1";
final String suspendingPackage2 = "package2";
final String dialogMessage1 = "dialogMessage1";
final String dialogMessage2 = "dialogMessage2";
final PackageUserState testUserState1 = new PackageUserState();
testUserState1.suspended = true;
testUserState1.suspendedAppExtras = appExtras1;
testUserState1.suspendedLauncherExtras = launcherExtras1;
testUserState1.suspendingPackage = suspendingPackage1;
testUserState1.dialogMessage = dialogMessage1;
final PackageUserState testUserState2 = new PackageUserState(testUserState1);
PackageUserState testUserState2 = new PackageUserState(testUserState1);
assertThat(testUserState1.equals(testUserState2), is(true));
testUserState2.suspendingPackage = suspendingPackage2;
assertThat(testUserState1.equals(testUserState2), is(false));
testUserState2.suspendingPackage = testUserState1.suspendingPackage;
testUserState2 = new PackageUserState(testUserState1);
testUserState2.suspendedAppExtras = appExtras2;
assertThat(testUserState1.equals(testUserState2), is(false));
testUserState2.suspendedAppExtras = testUserState1.suspendedAppExtras;
testUserState2 = new PackageUserState(testUserState1);
testUserState2.suspendedLauncherExtras = launcherExtras2;
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.suspendedAppExtras = appExtras2;
// Everything is different but irrelevant if suspended is false
testUserState2.suspendingPackage = suspendingPackage2;
testUserState2.dialogMessage = dialogMessage2;
testUserState2.suspendedAppExtras = appExtras2;
testUserState2.suspendedLauncherExtras = launcherExtras2;
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.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import android.app.AppGlobals;
import android.content.BroadcastReceiver;
@@ -41,8 +42,15 @@ import android.os.UserHandle;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.LargeTest;
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.view.IWindowManager;
import android.view.WindowManagerGlobal;
import com.android.servicestests.apps.suspendtestapp.SuspendTestActivity;
import com.android.servicestests.apps.suspendtestapp.SuspendTestReceiver;
import org.junit.After;
@@ -68,14 +76,25 @@ public class SuspendPackagesTest {
INSTRUMENTATION_PACKAGE + ".action.REPORT_MY_PACKAGE_SUSPENDED";
public static final String 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 PackageManager mPackageManager;
private LauncherApps mLauncherApps;
private Handler mReceiverHandler;
private ComponentName mTestReceiverComponent;
private AppCommunicationReceiver mAppCommsReceiver;
private StubbedCallback mTestCallback;
private UiDevice mUiDevice;
private static final class AppCommunicationReceiver extends BroadcastReceiver {
private Context context;
@@ -86,11 +105,12 @@ public class SuspendPackagesTest {
this.context = context;
}
void register(Handler handler) {
void register(Handler handler, String... actions) {
registered = true;
final IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(ACTION_REPORT_MY_PACKAGE_SUSPENDED);
intentFilter.addAction(ACTION_REPORT_MY_PACKAGE_UNSUSPENDED);
for (String action : actions) {
intentFilter.addAction(action);
}
context.registerReceiver(this, intentFilter, null, handler);
}
@@ -110,19 +130,28 @@ public class SuspendPackagesTest {
}
}
Intent receiveIntentFromApp() {
Intent pollForIntent(long secondsToWait) {
if (!registered) {
throw new IllegalStateException("Receiver not registered");
}
final Intent intent;
try {
intent = intentQueue.poll(5, TimeUnit.SECONDS);
intent = intentQueue.poll(secondsToWait, TimeUnit.SECONDS);
} catch (InterruptedException ie) {
throw new RuntimeException("Interrupted while waiting for app broadcast", ie);
}
assertNotNull("No intent received from app within 5 seconds", 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
@@ -131,8 +160,7 @@ public class SuspendPackagesTest {
mPackageManager = mContext.getPackageManager();
mLauncherApps = (LauncherApps) mContext.getSystemService(Context.LAUNCHER_APPS_SERVICE);
mReceiverHandler = new Handler(Looper.getMainLooper());
mTestReceiverComponent = new ComponentName(TEST_APP_PACKAGE_NAME,
SuspendTestReceiver.class.getCanonicalName());
mUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
IPackageManager ipm = AppGlobals.getPackageManager();
try {
// Otherwise implicit broadcasts will not be delivered.
@@ -151,9 +179,10 @@ public class SuspendPackagesTest {
private Bundle requestAppAction(String action) throws InterruptedException {
final AtomicReference<Bundle> result = new AtomicReference<>();
final CountDownLatch receiverLatch = new CountDownLatch(1);
final ComponentName testReceiverComponent = new ComponentName(TEST_APP_PACKAGE_NAME,
SuspendTestReceiver.class.getCanonicalName());
final Intent broadcastIntent = new Intent(action)
.setComponent(mTestReceiverComponent)
.setComponent(testReceiverComponent)
.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
mContext.sendOrderedBroadcast(broadcastIntent, null, new BroadcastReceiver() {
@Override
@@ -175,9 +204,10 @@ public class SuspendPackagesTest {
return extras;
}
private void suspendTestPackage(PersistableBundle appExtras, PersistableBundle launcherExtras) {
private void suspendTestPackage(PersistableBundle appExtras, PersistableBundle launcherExtras,
String dialogMessage) {
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);
}
@@ -187,6 +217,13 @@ public class SuspendPackagesTest {
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) {
if (expected != null) {
@@ -199,13 +236,14 @@ public class SuspendPackagesTest {
}
private static void assertSameExtras(String message, BaseBundle expected, BaseBundle received) {
assertTrue(message + ": [expected: " + expected + "; received: " + received + "]",
areSameExtras(expected, received));
if (!areSameExtras(expected, received)) {
fail(message + ": [expected: " + expected + "; received: " + received + "]");
}
}
@Test
public void testIsPackageSuspended() {
suspendTestPackage(null, null);
suspendTestPackage(null, null, null);
assertTrue("isPackageSuspended is false",
mPackageManager.isPackageSuspended(TEST_APP_PACKAGE_NAME));
}
@@ -217,7 +255,7 @@ public class SuspendPackagesTest {
assertNull(resultFromApp.getBundle(SuspendTestReceiver.EXTRA_SUSPENDED_APP_EXTRAS));
final PersistableBundle appExtras = getExtras("testSuspendedStateFromApp", 20, "20", 0.2);
suspendTestPackage(appExtras, null);
suspendTestPackage(appExtras, null, null);
resultFromApp = requestAppAction(SuspendTestReceiver.ACTION_GET_SUSPENDED_STATE);
assertTrue("resultFromApp:suspended is false",
@@ -230,37 +268,39 @@ public class SuspendPackagesTest {
@Test
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);
suspendTestPackage(appExtras, null);
suspendTestPackage(appExtras, null, null);
Intent intentFromApp = mAppCommsReceiver.receiveIntentFromApp();
assertTrue("MY_PACKAGE_SUSPENDED delivery not reported",
ACTION_REPORT_MY_PACKAGE_SUSPENDED.equals(intentFromApp.getAction()));
assertEquals("MY_PACKAGE_SUSPENDED delivery not reported",
ACTION_REPORT_MY_PACKAGE_SUSPENDED, intentFromApp.getAction());
assertSameExtras("Received app extras different to the ones supplied", appExtras,
intentFromApp.getBundleExtra(SuspendTestReceiver.EXTRA_SUSPENDED_APP_EXTRAS));
unsuspendTestPackage();
intentFromApp = mAppCommsReceiver.receiveIntentFromApp();
assertTrue("MY_PACKAGE_UNSUSPENDED delivery not reported",
ACTION_REPORT_MY_PACKAGE_UNSUSPENDED.equals(intentFromApp.getAction()));
assertEquals("MY_PACKAGE_UNSUSPENDED delivery not reported",
ACTION_REPORT_MY_PACKAGE_UNSUSPENDED, intentFromApp.getAction());
}
@Test
public void testUpdatingAppExtras() {
mAppCommsReceiver.register(mReceiverHandler);
mAppCommsReceiver.register(mReceiverHandler, ACTION_REPORT_MY_PACKAGE_SUSPENDED);
final PersistableBundle extras1 = getExtras("testMyPackageSuspendedOnChangingExtras", 1,
"1", 0.1);
suspendTestPackage(extras1, null);
suspendTestPackage(extras1, null, null);
Intent intentFromApp = mAppCommsReceiver.receiveIntentFromApp();
assertTrue("MY_PACKAGE_SUSPENDED delivery not reported",
ACTION_REPORT_MY_PACKAGE_SUSPENDED.equals(intentFromApp.getAction()));
assertEquals("MY_PACKAGE_SUSPENDED delivery not reported",
ACTION_REPORT_MY_PACKAGE_SUSPENDED, intentFromApp.getAction());
assertSameExtras("Received app extras different to the ones supplied", extras1,
intentFromApp.getBundleExtra(SuspendTestReceiver.EXTRA_SUSPENDED_APP_EXTRAS));
final PersistableBundle extras2 = getExtras("testMyPackageSuspendedOnChangingExtras", 2,
"2", 0.2);
mPackageManager.setSuspendedPackageAppExtras(TEST_APP_PACKAGE_NAME, extras2);
intentFromApp = mAppCommsReceiver.receiveIntentFromApp();
assertTrue("MY_PACKAGE_SUSPENDED delivery not reported",
ACTION_REPORT_MY_PACKAGE_SUSPENDED.equals(intentFromApp.getAction()));
assertEquals("MY_PACKAGE_SUSPENDED delivery not reported",
ACTION_REPORT_MY_PACKAGE_SUSPENDED, intentFromApp.getAction());
assertSameExtras("Received app extras different to the updated extras", extras2,
intentFromApp.getBundleExtra(SuspendTestReceiver.EXTRA_SUSPENDED_APP_EXTRAS));
}
@@ -273,13 +313,27 @@ public class SuspendPackagesTest {
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
public void testGetLauncherExtrasNonNull() {
final Bundle extrasWhenUnsuspended = mLauncherApps.getSuspendedPackageLauncherExtras(
TEST_APP_PACKAGE_NAME, mContext.getUser());
assertNull("Non null extras when package unsuspended:", extrasWhenUnsuspended);
final PersistableBundle launcherExtras = getExtras("testGetLauncherExtras", 1, "1", 0.1);
suspendTestPackage(null, launcherExtras);
suspendTestPackage(null, launcherExtras, null);
final Bundle receivedExtras = mLauncherApps.getSuspendedPackageLauncherExtras(
TEST_APP_PACKAGE_NAME, mContext.getUser());
assertSameExtras("Received launcher extras different to the ones supplied", launcherExtras,
@@ -288,7 +342,7 @@ public class SuspendPackagesTest {
@Test
public void testGetLauncherExtrasNull() {
suspendTestPackage(null, null);
suspendTestPackage(null, null, null);
final Bundle extrasWhenNoneGiven = mLauncherApps.getSuspendedPackageLauncherExtras(
TEST_APP_PACKAGE_NAME, mContext.getUser());
assertNull("Non null extras when null extras provided:", extrasWhenNoneGiven);
@@ -339,7 +393,7 @@ public class SuspendPackagesTest {
}
};
mLauncherApps.registerCallback(mTestCallback, mReceiverHandler);
suspendTestPackage(null, suppliedExtras);
suspendTestPackage(null, suppliedExtras, null);
assertFalse("Both callbacks were invoked", twoCallbackLatch.await(5, TimeUnit.SECONDS));
twoCallbackLatch.countDown();
assertTrue("No callback was invoked", twoCallbackLatch.await(2, TimeUnit.SECONDS));
@@ -373,19 +427,53 @@ public class SuspendPackagesTest {
}
};
mLauncherApps.registerCallback(mTestCallback, mReceiverHandler);
suspendTestPackage(null, suppliedExtras);
suspendTestPackage(null, suppliedExtras, null);
assertTrue("Callback not invoked", oneCallbackLatch.await(5, TimeUnit.SECONDS));
final String result = overridingOneCallbackResult.get();
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
public void tearDown() throws Exception {
public void tearDown() {
mAppCommsReceiver.unregister();
if (mTestCallback != null) {
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 {

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_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 += ../../src/com/android/server/pm/SuspendPackagesTest.java

View File

@@ -16,9 +16,45 @@
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.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.util.Log;
public class SuspendTestActivity extends Activity {
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();
}
}