Merge "Suspending app can customize intercepting dialog"
This commit is contained in:
committed by
Android (Google) Code Review
commit
bc9ebba7e5
@@ -1151,7 +1151,8 @@ package android.content.pm {
|
||||
method public abstract void revokeRuntimePermission(java.lang.String, java.lang.String, android.os.UserHandle);
|
||||
method public abstract boolean setDefaultBrowserPackageNameAsUser(java.lang.String, int);
|
||||
method public void setHarmfulAppWarning(java.lang.String, java.lang.CharSequence);
|
||||
method public java.lang.String[] setPackagesSuspended(java.lang.String[], boolean, android.os.PersistableBundle, android.os.PersistableBundle, java.lang.String);
|
||||
method public deprecated java.lang.String[] setPackagesSuspended(java.lang.String[], boolean, android.os.PersistableBundle, android.os.PersistableBundle, java.lang.String);
|
||||
method public java.lang.String[] setPackagesSuspended(java.lang.String[], boolean, android.os.PersistableBundle, android.os.PersistableBundle, android.content.pm.SuspendDialogInfo);
|
||||
method public abstract void setUpdateAvailable(java.lang.String, boolean);
|
||||
method public abstract boolean updateIntentVerificationStatusAsUser(java.lang.String, int, int);
|
||||
method public abstract void updatePermissionFlags(java.lang.String, java.lang.String, int, int, android.os.UserHandle);
|
||||
@@ -1245,6 +1246,22 @@ package android.content.pm {
|
||||
field public int requestRes;
|
||||
}
|
||||
|
||||
public final class SuspendDialogInfo implements android.os.Parcelable {
|
||||
method public int describeContents();
|
||||
method public void writeToParcel(android.os.Parcel, int);
|
||||
field public static final android.os.Parcelable.Creator<android.content.pm.SuspendDialogInfo> CREATOR;
|
||||
}
|
||||
|
||||
public static final class SuspendDialogInfo.Builder {
|
||||
ctor public SuspendDialogInfo.Builder();
|
||||
method public android.content.pm.SuspendDialogInfo build();
|
||||
method public android.content.pm.SuspendDialogInfo.Builder setIcon(int);
|
||||
method public android.content.pm.SuspendDialogInfo.Builder setMessage(java.lang.String);
|
||||
method public android.content.pm.SuspendDialogInfo.Builder setMessage(int);
|
||||
method public android.content.pm.SuspendDialogInfo.Builder setNeutralButtonText(int);
|
||||
method public android.content.pm.SuspendDialogInfo.Builder setTitle(int);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
package android.content.pm.dex {
|
||||
|
||||
@@ -55,6 +55,7 @@ import android.content.pm.ProviderInfo;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.content.pm.ServiceInfo;
|
||||
import android.content.pm.SharedLibraryInfo;
|
||||
import android.content.pm.SuspendDialogInfo;
|
||||
import android.content.pm.VerifierDeviceIdentity;
|
||||
import android.content.pm.VersionedPackage;
|
||||
import android.content.pm.dex.ArtManager;
|
||||
@@ -85,6 +86,7 @@ import android.system.ErrnoException;
|
||||
import android.system.Os;
|
||||
import android.system.OsConstants;
|
||||
import android.system.StructStat;
|
||||
import android.text.TextUtils;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.IconDrawableFactory;
|
||||
import android.util.LauncherIcons;
|
||||
@@ -2255,9 +2257,19 @@ public class ApplicationPackageManager extends PackageManager {
|
||||
public String[] setPackagesSuspended(String[] packageNames, boolean suspended,
|
||||
PersistableBundle appExtras, PersistableBundle launcherExtras,
|
||||
String dialogMessage) {
|
||||
final SuspendDialogInfo dialogInfo = !TextUtils.isEmpty(dialogMessage)
|
||||
? new SuspendDialogInfo.Builder().setMessage(dialogMessage).build()
|
||||
: null;
|
||||
return setPackagesSuspended(packageNames, suspended, appExtras, launcherExtras, dialogInfo);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] setPackagesSuspended(String[] packageNames, boolean suspended,
|
||||
PersistableBundle appExtras, PersistableBundle launcherExtras,
|
||||
SuspendDialogInfo dialogInfo) {
|
||||
try {
|
||||
return mPM.setPackagesSuspendedAsUser(packageNames, suspended, appExtras,
|
||||
launcherExtras, dialogMessage, mContext.getOpPackageName(),
|
||||
launcherExtras, dialogInfo, mContext.getOpPackageName(),
|
||||
getUserId());
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
|
||||
@@ -43,6 +43,7 @@ import android.content.pm.PermissionGroupInfo;
|
||||
import android.content.pm.PermissionInfo;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.content.pm.ServiceInfo;
|
||||
import android.content.pm.SuspendDialogInfo;
|
||||
import android.content.pm.UserInfo;
|
||||
import android.content.pm.VerifierDeviceIdentity;
|
||||
import android.content.pm.VersionedPackage;
|
||||
@@ -273,7 +274,7 @@ interface IPackageManager {
|
||||
|
||||
String[] setPackagesSuspendedAsUser(in String[] packageNames, boolean suspended,
|
||||
in PersistableBundle appExtras, in PersistableBundle launcherExtras,
|
||||
String dialogMessage, String callingPackage, int userId);
|
||||
in SuspendDialogInfo dialogInfo, String callingPackage, int userId);
|
||||
|
||||
boolean isPackageSuspendedForUser(String packageName, int userId);
|
||||
|
||||
|
||||
@@ -5664,7 +5664,7 @@ public abstract class PackageManager {
|
||||
* {@link Manifest.permission#MANAGE_USERS} to use this api.</p>
|
||||
*
|
||||
* @param packageNames The names of the packages to set the suspended status.
|
||||
* @param suspended If set to {@code true} than the packages will be suspended, if set to
|
||||
* @param suspended If set to {@code true}, the packages will be suspended, if set to
|
||||
* {@code false}, the packages will be unsuspended.
|
||||
* @param appExtras An optional {@link PersistableBundle} that the suspending app can provide
|
||||
* which will be shared with the apps being suspended. Ignored if
|
||||
@@ -5676,15 +5676,76 @@ public abstract class PackageManager {
|
||||
* suspended app.
|
||||
*
|
||||
* @return an array of package names for which the suspended status could not be set as
|
||||
* requested in this method.
|
||||
* requested in this method. Returns {@code null} if {@code packageNames} was {@code null}.
|
||||
*
|
||||
* @deprecated use {@link #setPackagesSuspended(String[], boolean, PersistableBundle,
|
||||
* PersistableBundle, android.content.pm.SuspendDialogInfo)} instead.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
@Deprecated
|
||||
@RequiresPermission(Manifest.permission.SUSPEND_APPS)
|
||||
@Nullable
|
||||
public String[] setPackagesSuspended(@Nullable String[] packageNames, boolean suspended,
|
||||
@Nullable PersistableBundle appExtras, @Nullable PersistableBundle launcherExtras,
|
||||
@Nullable String dialogMessage) {
|
||||
throw new UnsupportedOperationException("setPackagesSuspended not implemented");
|
||||
}
|
||||
|
||||
/**
|
||||
* Puts the given packages in a suspended state, where attempts at starting activities are
|
||||
* denied.
|
||||
*
|
||||
* <p>The suspended application's notifications and all of its windows will be hidden, any
|
||||
* of its started activities will be stopped and it won't be able to ring the device.
|
||||
* It doesn't remove the data or the actual package file.
|
||||
*
|
||||
* <p>When the user tries to launch a suspended app, a system dialog alerting them that the app
|
||||
* is suspended will be shown instead.
|
||||
* The caller can optionally customize the dialog by passing a {@link SuspendDialogInfo} object
|
||||
* to this api. This dialog will have a button that starts the
|
||||
* {@link Intent#ACTION_SHOW_SUSPENDED_APP_DETAILS} intent if the suspending app declares an
|
||||
* activity which handles this action.
|
||||
*
|
||||
* <p>The packages being suspended must already be installed. If a package is uninstalled, it
|
||||
* will no longer be suspended.
|
||||
*
|
||||
* <p>Optionally, the suspending app can provide extra information in the form of
|
||||
* {@link PersistableBundle} objects to be shared with the apps being suspended and the
|
||||
* launcher to support customization that they might need to handle the suspended state.
|
||||
*
|
||||
* <p>The caller must hold {@link Manifest.permission#SUSPEND_APPS} to use this api.
|
||||
*
|
||||
* @param packageNames The names of the packages to set the suspended status.
|
||||
* @param suspended If set to {@code true}, the packages will be suspended, if set to
|
||||
* {@code false}, the packages will be unsuspended.
|
||||
* @param appExtras An optional {@link PersistableBundle} that the suspending app can provide
|
||||
* which will be shared with the apps being suspended. Ignored if
|
||||
* {@code suspended} is false.
|
||||
* @param launcherExtras An optional {@link PersistableBundle} that the suspending app can
|
||||
* provide which will be shared with the launcher. Ignored if
|
||||
* {@code suspended} is false.
|
||||
* @param dialogInfo An optional {@link SuspendDialogInfo} object describing the dialog that
|
||||
* should be shown to the user when they try to launch a suspended app.
|
||||
* Ignored if {@code suspended} is false.
|
||||
*
|
||||
* @return an array of package names for which the suspended status could not be set as
|
||||
* requested in this method. Returns {@code null} if {@code packageNames} was {@code null}.
|
||||
*
|
||||
* @see #isPackageSuspended
|
||||
* @see SuspendDialogInfo
|
||||
* @see SuspendDialogInfo.Builder
|
||||
* @see Intent#ACTION_SHOW_SUSPENDED_APP_DETAILS
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@SystemApi
|
||||
@RequiresPermission(Manifest.permission.SUSPEND_APPS)
|
||||
public String[] setPackagesSuspended(String[] packageNames, boolean suspended,
|
||||
@Nullable
|
||||
public String[] setPackagesSuspended(@Nullable String[] packageNames, boolean suspended,
|
||||
@Nullable PersistableBundle appExtras, @Nullable PersistableBundle launcherExtras,
|
||||
String dialogMessage) {
|
||||
@Nullable SuspendDialogInfo dialogInfo) {
|
||||
throw new UnsupportedOperationException("setPackagesSuspended not implemented");
|
||||
}
|
||||
|
||||
|
||||
@@ -267,14 +267,15 @@ public abstract class PackageManagerInternal {
|
||||
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.
|
||||
* Get the information describing the dialog 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.
|
||||
* @return A {@link SuspendDialogInfo} object describing the dialog to be shown.
|
||||
*/
|
||||
public abstract String getSuspendedDialogMessage(String suspendedPackage, int userId);
|
||||
@Nullable
|
||||
public abstract SuspendDialogInfo getSuspendedDialogInfo(String suspendedPackage, int userId);
|
||||
|
||||
/**
|
||||
* Do a straight uid lookup for the given package/application in the given user.
|
||||
|
||||
@@ -33,6 +33,7 @@ import android.os.BaseBundle;
|
||||
import android.os.PersistableBundle;
|
||||
import android.util.ArraySet;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.util.ArrayUtils;
|
||||
|
||||
import java.util.Arrays;
|
||||
@@ -50,7 +51,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 SuspendDialogInfo dialogInfo;
|
||||
public PersistableBundle suspendedAppExtras;
|
||||
public PersistableBundle suspendedLauncherExtras;
|
||||
public boolean instantApp;
|
||||
@@ -79,6 +80,7 @@ public class PackageUserState {
|
||||
installReason = PackageManager.INSTALL_REASON_UNKNOWN;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public PackageUserState(PackageUserState o) {
|
||||
ceDataInode = o.ceDataInode;
|
||||
installed = o.installed;
|
||||
@@ -87,7 +89,7 @@ public class PackageUserState {
|
||||
hidden = o.hidden;
|
||||
suspended = o.suspended;
|
||||
suspendingPackage = o.suspendingPackage;
|
||||
dialogMessage = o.dialogMessage;
|
||||
dialogInfo = o.dialogInfo;
|
||||
suspendedAppExtras = o.suspendedAppExtras;
|
||||
suspendedLauncherExtras = o.suspendedLauncherExtras;
|
||||
instantApp = o.instantApp;
|
||||
@@ -217,7 +219,7 @@ public class PackageUserState {
|
||||
|| !suspendingPackage.equals(oldState.suspendingPackage)) {
|
||||
return false;
|
||||
}
|
||||
if (!Objects.equals(dialogMessage, oldState.dialogMessage)) {
|
||||
if (!Objects.equals(dialogInfo, oldState.dialogInfo)) {
|
||||
return false;
|
||||
}
|
||||
if (!BaseBundle.kindofEquals(suspendedAppExtras,
|
||||
|
||||
18
core/java/android/content/pm/SuspendDialogInfo.aidl
Normal file
18
core/java/android/content/pm/SuspendDialogInfo.aidl
Normal file
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* 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 android.content.pm;
|
||||
|
||||
parcelable SuspendDialogInfo;
|
||||
379
core/java/android/content/pm/SuspendDialogInfo.java
Normal file
379
core/java/android/content/pm/SuspendDialogInfo.java
Normal file
@@ -0,0 +1,379 @@
|
||||
/*
|
||||
* 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 android.content.pm;
|
||||
|
||||
import static android.content.res.ResourceId.ID_NULL;
|
||||
|
||||
import android.annotation.DrawableRes;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.annotation.StringRes;
|
||||
import android.annotation.SystemApi;
|
||||
import android.content.res.ResourceId;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.os.PersistableBundle;
|
||||
import android.util.Slog;
|
||||
|
||||
import com.android.internal.util.Preconditions;
|
||||
import com.android.internal.util.XmlUtils;
|
||||
|
||||
import org.xmlpull.v1.XmlPullParser;
|
||||
import org.xmlpull.v1.XmlSerializer;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* A container to describe the dialog to be shown when the user tries to launch a suspended
|
||||
* application.
|
||||
* The suspending app can customize the dialog's following attributes:
|
||||
* <ul>
|
||||
* <li>The dialog icon, by providing a resource id.
|
||||
* <li>The title text, by providing a resource id.
|
||||
* <li>The text of the dialog's body, by providing a resource id or a string.
|
||||
* <li>The text on the neutral button which starts the
|
||||
* {@link android.content.Intent#ACTION_SHOW_SUSPENDED_APP_DETAILS SHOW_SUSPENDED_APP_DETAILS}
|
||||
* activity, by providing a resource id.
|
||||
* </ul>
|
||||
* System defaults are used whenever any of these are not provided, or any of the provided resource
|
||||
* ids cannot be resolved at the time of displaying the dialog.
|
||||
*
|
||||
* @hide
|
||||
* @see PackageManager#setPackagesSuspended(String[], boolean, PersistableBundle, PersistableBundle,
|
||||
* SuspendDialogInfo)
|
||||
* @see Builder
|
||||
*/
|
||||
@SystemApi
|
||||
public final class SuspendDialogInfo implements Parcelable {
|
||||
private static final String TAG = SuspendDialogInfo.class.getSimpleName();
|
||||
private static final String XML_ATTR_ICON_RES_ID = "iconResId";
|
||||
private static final String XML_ATTR_TITLE_RES_ID = "titleResId";
|
||||
private static final String XML_ATTR_DIALOG_MESSAGE_RES_ID = "dialogMessageResId";
|
||||
private static final String XML_ATTR_DIALOG_MESSAGE = "dialogMessage";
|
||||
private static final String XML_ATTR_BUTTON_TEXT_RES_ID = "buttonTextResId";
|
||||
|
||||
private final int mIconResId;
|
||||
private final int mTitleResId;
|
||||
private final int mDialogMessageResId;
|
||||
private final String mDialogMessage;
|
||||
private final int mNeutralButtonTextResId;
|
||||
|
||||
/**
|
||||
* @return the resource id of the icon to be used with the dialog
|
||||
* @hide
|
||||
*/
|
||||
@DrawableRes
|
||||
public int getIconResId() {
|
||||
return mIconResId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the resource id of the title to be used with the dialog
|
||||
* @hide
|
||||
*/
|
||||
@StringRes
|
||||
public int getTitleResId() {
|
||||
return mTitleResId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the resource id of the text to be shown in the dialog's body
|
||||
* @hide
|
||||
*/
|
||||
@StringRes
|
||||
public int getDialogMessageResId() {
|
||||
return mDialogMessageResId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the text to be shown in the dialog's body. Returns {@code null} if
|
||||
* {@link #getDialogMessageResId()} returns a valid resource id.
|
||||
* @hide
|
||||
*/
|
||||
@Nullable
|
||||
public String getDialogMessage() {
|
||||
return mDialogMessage;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the text to be shown
|
||||
* @hide
|
||||
*/
|
||||
@StringRes
|
||||
public int getNeutralButtonTextResId() {
|
||||
return mNeutralButtonTextResId;
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public void saveToXml(XmlSerializer out) throws IOException {
|
||||
if (mIconResId != ID_NULL) {
|
||||
XmlUtils.writeIntAttribute(out, XML_ATTR_ICON_RES_ID, mIconResId);
|
||||
}
|
||||
if (mTitleResId != ID_NULL) {
|
||||
XmlUtils.writeIntAttribute(out, XML_ATTR_TITLE_RES_ID, mTitleResId);
|
||||
}
|
||||
if (mDialogMessageResId != ID_NULL) {
|
||||
XmlUtils.writeIntAttribute(out, XML_ATTR_DIALOG_MESSAGE_RES_ID, mDialogMessageResId);
|
||||
} else {
|
||||
XmlUtils.writeStringAttribute(out, XML_ATTR_DIALOG_MESSAGE, mDialogMessage);
|
||||
}
|
||||
if (mNeutralButtonTextResId != ID_NULL) {
|
||||
XmlUtils.writeIntAttribute(out, XML_ATTR_BUTTON_TEXT_RES_ID, mNeutralButtonTextResId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public static SuspendDialogInfo restoreFromXml(XmlPullParser in) {
|
||||
final SuspendDialogInfo.Builder dialogInfoBuilder = new SuspendDialogInfo.Builder();
|
||||
try {
|
||||
final int iconId = XmlUtils.readIntAttribute(in, XML_ATTR_ICON_RES_ID, ID_NULL);
|
||||
final int titleId = XmlUtils.readIntAttribute(in, XML_ATTR_TITLE_RES_ID, ID_NULL);
|
||||
final int buttonTextId = XmlUtils.readIntAttribute(in, XML_ATTR_BUTTON_TEXT_RES_ID,
|
||||
ID_NULL);
|
||||
final int dialogMessageResId = XmlUtils.readIntAttribute(
|
||||
in, XML_ATTR_DIALOG_MESSAGE_RES_ID, ID_NULL);
|
||||
final String dialogMessage = XmlUtils.readStringAttribute(in, XML_ATTR_DIALOG_MESSAGE);
|
||||
|
||||
if (iconId != ID_NULL) {
|
||||
dialogInfoBuilder.setIcon(iconId);
|
||||
}
|
||||
if (titleId != ID_NULL) {
|
||||
dialogInfoBuilder.setTitle(titleId);
|
||||
}
|
||||
if (buttonTextId != ID_NULL) {
|
||||
dialogInfoBuilder.setNeutralButtonText(buttonTextId);
|
||||
}
|
||||
if (dialogMessageResId != ID_NULL) {
|
||||
dialogInfoBuilder.setMessage(dialogMessageResId);
|
||||
} else if (dialogMessage != null) {
|
||||
dialogInfoBuilder.setMessage(dialogMessage);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Slog.e(TAG, "Exception while parsing from xml. Some fields may default", e);
|
||||
}
|
||||
return dialogInfoBuilder.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hashCode = mIconResId;
|
||||
hashCode = 31 * hashCode + mTitleResId;
|
||||
hashCode = 31 * hashCode + mNeutralButtonTextResId;
|
||||
hashCode = 31 * hashCode + mDialogMessageResId;
|
||||
hashCode = 31 * hashCode + Objects.hashCode(mDialogMessage);
|
||||
return hashCode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj) {
|
||||
return true;
|
||||
}
|
||||
if (!(obj instanceof SuspendDialogInfo)) {
|
||||
return false;
|
||||
}
|
||||
final SuspendDialogInfo otherDialogInfo = (SuspendDialogInfo) obj;
|
||||
return mIconResId == otherDialogInfo.mIconResId
|
||||
&& mTitleResId == otherDialogInfo.mTitleResId
|
||||
&& mDialogMessageResId == otherDialogInfo.mDialogMessageResId
|
||||
&& mNeutralButtonTextResId == otherDialogInfo.mNeutralButtonTextResId
|
||||
&& Objects.equals(mDialogMessage, otherDialogInfo.mDialogMessage);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
final StringBuilder builder = new StringBuilder("SuspendDialogInfo: {");
|
||||
if (mIconResId != ID_NULL) {
|
||||
builder.append("mIconId = 0x");
|
||||
builder.append(Integer.toHexString(mIconResId));
|
||||
builder.append(" ");
|
||||
}
|
||||
if (mTitleResId != ID_NULL) {
|
||||
builder.append("mTitleResId = 0x");
|
||||
builder.append(Integer.toHexString(mTitleResId));
|
||||
builder.append(" ");
|
||||
}
|
||||
if (mNeutralButtonTextResId != ID_NULL) {
|
||||
builder.append("mNeutralButtonTextResId = 0x");
|
||||
builder.append(Integer.toHexString(mNeutralButtonTextResId));
|
||||
builder.append(" ");
|
||||
}
|
||||
if (mDialogMessageResId != ID_NULL) {
|
||||
builder.append("mDialogMessageResId = 0x");
|
||||
builder.append(Integer.toHexString(mDialogMessageResId));
|
||||
builder.append(" ");
|
||||
} else if (mDialogMessage != null) {
|
||||
builder.append("mDialogMessage = \"");
|
||||
builder.append(mDialogMessage);
|
||||
builder.append("\" ");
|
||||
}
|
||||
builder.append("}");
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int parcelableFlags) {
|
||||
dest.writeInt(mIconResId);
|
||||
dest.writeInt(mTitleResId);
|
||||
dest.writeInt(mDialogMessageResId);
|
||||
dest.writeString(mDialogMessage);
|
||||
dest.writeInt(mNeutralButtonTextResId);
|
||||
}
|
||||
|
||||
private SuspendDialogInfo(Parcel source) {
|
||||
mIconResId = source.readInt();
|
||||
mTitleResId = source.readInt();
|
||||
mDialogMessageResId = source.readInt();
|
||||
mDialogMessage = source.readString();
|
||||
mNeutralButtonTextResId = source.readInt();
|
||||
}
|
||||
|
||||
SuspendDialogInfo(Builder b) {
|
||||
mIconResId = b.mIconResId;
|
||||
mTitleResId = b.mTitleResId;
|
||||
mDialogMessageResId = b.mDialogMessageResId;
|
||||
mDialogMessage = (mDialogMessageResId == ID_NULL) ? b.mDialogMessage : null;
|
||||
mNeutralButtonTextResId = b.mNeutralButtonTextResId;
|
||||
}
|
||||
|
||||
public static final Creator<SuspendDialogInfo> CREATOR = new Creator<SuspendDialogInfo>() {
|
||||
@Override
|
||||
public SuspendDialogInfo createFromParcel(Parcel source) {
|
||||
return new SuspendDialogInfo(source);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SuspendDialogInfo[] newArray(int size) {
|
||||
return new SuspendDialogInfo[size];
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Builder to build a {@link SuspendDialogInfo} object.
|
||||
*/
|
||||
public static final class Builder {
|
||||
private int mDialogMessageResId = ID_NULL;
|
||||
private String mDialogMessage;
|
||||
private int mTitleResId = ID_NULL;
|
||||
private int mIconResId = ID_NULL;
|
||||
private int mNeutralButtonTextResId = ID_NULL;
|
||||
|
||||
/**
|
||||
* Set the resource id of the icon to be used. If not provided, no icon will be shown.
|
||||
*
|
||||
* @param resId The resource id of the icon.
|
||||
* @return this builder object.
|
||||
*/
|
||||
@NonNull
|
||||
public Builder setIcon(@DrawableRes int resId) {
|
||||
Preconditions.checkArgument(ResourceId.isValid(resId), "Invalid resource id provided");
|
||||
mIconResId = resId;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the resource id of the title text to be displayed. If this is not provided, the
|
||||
* system will use a default title.
|
||||
*
|
||||
* @param resId The resource id of the title.
|
||||
* @return this builder object.
|
||||
*/
|
||||
@NonNull
|
||||
public Builder setTitle(@StringRes int resId) {
|
||||
Preconditions.checkArgument(ResourceId.isValid(resId), "Invalid resource id provided");
|
||||
mTitleResId = resId;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the text to show in the body of the dialog. Ignored if a resource id is set via
|
||||
* {@link #setMessage(int)}.
|
||||
* <p>
|
||||
* The system will use {@link String#format(Locale, String, Object...) String.format} to
|
||||
* insert the suspended app name into the message, so an example format string could be
|
||||
* {@code "The app %1$s is currently suspended"}. This is optional - if the string passed in
|
||||
* {@code message} does not accept an argument, it will be used as is.
|
||||
*
|
||||
* @param message The dialog message.
|
||||
* @return this builder object.
|
||||
* @see #setMessage(int)
|
||||
*/
|
||||
@NonNull
|
||||
public Builder setMessage(@NonNull String message) {
|
||||
Preconditions.checkStringNotEmpty(message, "Message cannot be null or empty");
|
||||
mDialogMessage = message;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the resource id of the dialog message to be shown. If no dialog message is provided
|
||||
* via either this method or {@link #setMessage(String)}, the system will use a
|
||||
* default message.
|
||||
* <p>
|
||||
* The system will use {@link android.content.res.Resources#getString(int, Object...)
|
||||
* getString} to insert the suspended app name into the message, so an example format string
|
||||
* could be {@code "The app %1$s is currently suspended"}. This is optional - if the string
|
||||
* referred to by {@code resId} does not accept an argument, it will be used as is.
|
||||
*
|
||||
* @param resId The resource id of the dialog message.
|
||||
* @return this builder object.
|
||||
* @see #setMessage(String)
|
||||
*/
|
||||
@NonNull
|
||||
public Builder setMessage(@StringRes int resId) {
|
||||
Preconditions.checkArgument(ResourceId.isValid(resId), "Invalid resource id provided");
|
||||
mDialogMessageResId = resId;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the resource id of text to be shown on the neutral button. Tapping this button starts
|
||||
* the {@link android.content.Intent#ACTION_SHOW_SUSPENDED_APP_DETAILS} activity. If this is
|
||||
* not provided, the system will use a default text.
|
||||
*
|
||||
* @param resId The resource id of the button text
|
||||
* @return this builder object.
|
||||
*/
|
||||
@NonNull
|
||||
public Builder setNeutralButtonText(@StringRes int resId) {
|
||||
Preconditions.checkArgument(ResourceId.isValid(resId), "Invalid resource id provided");
|
||||
mNeutralButtonTextResId = resId;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the final object based on given inputs.
|
||||
*
|
||||
* @return The {@link SuspendDialogInfo} object built using this builder.
|
||||
*/
|
||||
@NonNull
|
||||
public SuspendDialogInfo build() {
|
||||
return new SuspendDialogInfo(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -16,12 +16,17 @@
|
||||
|
||||
package com.android.internal.app;
|
||||
|
||||
import static android.content.res.ResourceId.ID_NULL;
|
||||
|
||||
import android.Manifest;
|
||||
import android.app.AlertDialog;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.content.pm.SuspendDialogInfo;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Bundle;
|
||||
import android.os.UserHandle;
|
||||
import android.util.Slog;
|
||||
@@ -31,16 +36,19 @@ import com.android.internal.R;
|
||||
|
||||
public class SuspendedAppActivity extends AlertActivity
|
||||
implements DialogInterface.OnClickListener {
|
||||
private static final String TAG = "SuspendedAppActivity";
|
||||
public static final String EXTRA_SUSPENDED_PACKAGE =
|
||||
"SuspendedAppActivity.extra.SUSPENDED_PACKAGE";
|
||||
private static final String TAG = SuspendedAppActivity.class.getSimpleName();
|
||||
private static final String PACKAGE_NAME = "com.android.internal.app";
|
||||
|
||||
public static final String EXTRA_SUSPENDED_PACKAGE = PACKAGE_NAME + ".extra.SUSPENDED_PACKAGE";
|
||||
public static final String EXTRA_SUSPENDING_PACKAGE =
|
||||
"SuspendedAppActivity.extra.SUSPENDING_PACKAGE";
|
||||
public static final String EXTRA_DIALOG_MESSAGE = "SuspendedAppActivity.extra.DIALOG_MESSAGE";
|
||||
PACKAGE_NAME + ".extra.SUSPENDING_PACKAGE";
|
||||
public static final String EXTRA_DIALOG_INFO = PACKAGE_NAME + ".extra.DIALOG_INFO";
|
||||
|
||||
private Intent mMoreDetailsIntent;
|
||||
private int mUserId;
|
||||
private PackageManager mPm;
|
||||
private Resources mSuspendingAppResources;
|
||||
private SuspendDialogInfo mSuppliedDialogInfo;
|
||||
|
||||
private CharSequence getAppLabel(String packageName) {
|
||||
try {
|
||||
@@ -66,6 +74,65 @@ public class SuspendedAppActivity extends AlertActivity
|
||||
return null;
|
||||
}
|
||||
|
||||
private Drawable resolveIcon() {
|
||||
final int iconId = (mSuppliedDialogInfo != null) ? mSuppliedDialogInfo.getIconResId()
|
||||
: ID_NULL;
|
||||
if (iconId != ID_NULL && mSuspendingAppResources != null) {
|
||||
try {
|
||||
return mSuspendingAppResources.getDrawable(iconId, null);
|
||||
} catch (Resources.NotFoundException nfe) {
|
||||
Slog.e(TAG, "Could not resolve drawable resource id " + iconId);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private String resolveTitle() {
|
||||
final int titleId = (mSuppliedDialogInfo != null) ? mSuppliedDialogInfo.getTitleResId()
|
||||
: ID_NULL;
|
||||
if (titleId != ID_NULL && mSuspendingAppResources != null) {
|
||||
try {
|
||||
return mSuspendingAppResources.getString(titleId);
|
||||
} catch (Resources.NotFoundException nfe) {
|
||||
Slog.e(TAG, "Could not resolve string resource id " + titleId);
|
||||
}
|
||||
}
|
||||
return getString(R.string.app_suspended_title);
|
||||
}
|
||||
|
||||
private String resolveDialogMessage(String suspendingPkg, String suspendedPkg) {
|
||||
final CharSequence suspendedAppLabel = getAppLabel(suspendedPkg);
|
||||
if (mSuppliedDialogInfo != null) {
|
||||
final int messageId = mSuppliedDialogInfo.getDialogMessageResId();
|
||||
final String message = mSuppliedDialogInfo.getDialogMessage();
|
||||
if (messageId != ID_NULL && mSuspendingAppResources != null) {
|
||||
try {
|
||||
return mSuspendingAppResources.getString(messageId, suspendedAppLabel);
|
||||
} catch (Resources.NotFoundException nfe) {
|
||||
Slog.e(TAG, "Could not resolve string resource id " + messageId);
|
||||
}
|
||||
} else if (message != null) {
|
||||
return String.format(getResources().getConfiguration().getLocales().get(0), message,
|
||||
suspendedAppLabel);
|
||||
}
|
||||
}
|
||||
return getString(R.string.app_suspended_default_message, suspendedAppLabel,
|
||||
getAppLabel(suspendingPkg));
|
||||
}
|
||||
|
||||
private String resolveNeutralButtonText() {
|
||||
final int buttonTextId = (mSuppliedDialogInfo != null)
|
||||
? mSuppliedDialogInfo.getNeutralButtonTextResId() : ID_NULL;
|
||||
if (buttonTextId != ID_NULL && mSuspendingAppResources != null) {
|
||||
try {
|
||||
return mSuspendingAppResources.getString(buttonTextId);
|
||||
} catch (Resources.NotFoundException nfe) {
|
||||
Slog.e(TAG, "Could not resolve string resource id " + buttonTextId);
|
||||
}
|
||||
}
|
||||
return getString(R.string.app_suspended_more_details);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle icicle) {
|
||||
super.onCreate(icicle);
|
||||
@@ -79,26 +146,26 @@ public class SuspendedAppActivity extends AlertActivity
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
final String suppliedMessage = intent.getStringExtra(EXTRA_DIALOG_MESSAGE);
|
||||
final String suspendedPackage = intent.getStringExtra(EXTRA_SUSPENDED_PACKAGE);
|
||||
final String suspendingPackage = intent.getStringExtra(EXTRA_SUSPENDING_PACKAGE);
|
||||
final CharSequence suspendedAppLabel = getAppLabel(suspendedPackage);
|
||||
final CharSequence dialogMessage;
|
||||
if (suppliedMessage == null) {
|
||||
dialogMessage = getString(R.string.app_suspended_default_message, suspendedAppLabel,
|
||||
getAppLabel(suspendingPackage));
|
||||
} else {
|
||||
dialogMessage = String.format(getResources().getConfiguration().getLocales().get(0),
|
||||
suppliedMessage, suspendedAppLabel);
|
||||
mSuppliedDialogInfo = intent.getParcelableExtra(EXTRA_DIALOG_INFO);
|
||||
if (mSuppliedDialogInfo != null) {
|
||||
try {
|
||||
mSuspendingAppResources = mPm.getResourcesForApplicationAsUser(suspendingPackage,
|
||||
mUserId);
|
||||
} catch (PackageManager.NameNotFoundException ne) {
|
||||
Slog.e(TAG, "Could not find resources for " + suspendingPackage, ne);
|
||||
}
|
||||
}
|
||||
|
||||
final AlertController.AlertParams ap = mAlertParams;
|
||||
ap.mTitle = getString(R.string.app_suspended_title);
|
||||
ap.mMessage = dialogMessage;
|
||||
ap.mIcon = resolveIcon();
|
||||
ap.mTitle = resolveTitle();
|
||||
ap.mMessage = resolveDialogMessage(suspendingPackage, suspendedPackage);
|
||||
ap.mPositiveButtonText = getString(android.R.string.ok);
|
||||
mMoreDetailsIntent = getMoreDetailsActivity(suspendingPackage, suspendedPackage, mUserId);
|
||||
if (mMoreDetailsIntent != null) {
|
||||
ap.mNeutralButtonText = getString(R.string.app_suspended_more_details);
|
||||
ap.mNeutralButtonText = resolveNeutralButtonText();
|
||||
}
|
||||
ap.mPositiveButtonListener = ap.mNeutralButtonListener = this;
|
||||
setupAlert();
|
||||
@@ -116,11 +183,11 @@ public class SuspendedAppActivity extends AlertActivity
|
||||
}
|
||||
|
||||
public static Intent createSuspendedAppInterceptIntent(String suspendedPackage,
|
||||
String suspendingPackage, String dialogMessage, int userId) {
|
||||
String suspendingPackage, SuspendDialogInfo dialogInfo, int userId) {
|
||||
return new Intent()
|
||||
.setClassName("android", SuspendedAppActivity.class.getName())
|
||||
.putExtra(EXTRA_SUSPENDED_PACKAGE, suspendedPackage)
|
||||
.putExtra(EXTRA_DIALOG_MESSAGE, dialogMessage)
|
||||
.putExtra(EXTRA_DIALOG_INFO, dialogInfo)
|
||||
.putExtra(EXTRA_SUSPENDING_PACKAGE, suspendingPackage)
|
||||
.putExtra(Intent.EXTRA_USER_ID, userId)
|
||||
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
|
||||
|
||||
@@ -56,6 +56,7 @@ import android.content.pm.ParceledListSlice;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.content.pm.ServiceInfo;
|
||||
import android.content.pm.ShortcutServiceInternal;
|
||||
import android.content.pm.SuspendDialogInfo;
|
||||
import android.content.pm.UserInfo;
|
||||
import android.content.res.Resources;
|
||||
import android.content.res.TypedArray;
|
||||
@@ -629,10 +630,10 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
|
||||
onClickIntent = mDevicePolicyManagerInternal.createShowAdminSupportIntent(
|
||||
providerUserId, true);
|
||||
} else {
|
||||
final String dialogMessage = mPackageManagerInternal.getSuspendedDialogMessage(
|
||||
providerPackage, providerUserId);
|
||||
final SuspendDialogInfo dialogInfo = mPackageManagerInternal
|
||||
.getSuspendedDialogInfo(providerPackage, providerUserId);
|
||||
onClickIntent = SuspendedAppActivity.createSuspendedAppInterceptIntent(
|
||||
providerPackage, suspendingPackage, dialogMessage, providerUserId);
|
||||
providerPackage, suspendingPackage, dialogInfo, providerUserId);
|
||||
}
|
||||
} else if (provider.maskedByQuietProfile) {
|
||||
showBadge = true;
|
||||
|
||||
@@ -44,6 +44,7 @@ import android.content.IntentSender;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.content.pm.PackageManagerInternal;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.content.pm.SuspendDialogInfo;
|
||||
import android.content.pm.UserInfo;
|
||||
import android.os.Binder;
|
||||
import android.os.Bundle;
|
||||
@@ -246,9 +247,9 @@ class ActivityStartInterceptor {
|
||||
if (PLATFORM_PACKAGE_NAME.equals(suspendingPackage)) {
|
||||
return interceptSuspendedByAdminPackage();
|
||||
}
|
||||
final String dialogMessage = pmi.getSuspendedDialogMessage(suspendedPackage, mUserId);
|
||||
final SuspendDialogInfo dialogInfo = pmi.getSuspendedDialogInfo(suspendedPackage, mUserId);
|
||||
mIntent = SuspendedAppActivity.createSuspendedAppInterceptIntent(suspendedPackage,
|
||||
suspendingPackage, dialogMessage, mUserId);
|
||||
suspendingPackage, dialogInfo, mUserId);
|
||||
mCallingPid = mRealCallingPid;
|
||||
mCallingUid = mRealCallingUid;
|
||||
mResolvedType = null;
|
||||
|
||||
@@ -188,6 +188,7 @@ import android.content.pm.SELinuxUtil;
|
||||
import android.content.pm.ServiceInfo;
|
||||
import android.content.pm.SharedLibraryInfo;
|
||||
import android.content.pm.Signature;
|
||||
import android.content.pm.SuspendDialogInfo;
|
||||
import android.content.pm.UserInfo;
|
||||
import android.content.pm.VerifierDeviceIdentity;
|
||||
import android.content.pm.VerifierInfo;
|
||||
@@ -12716,8 +12717,8 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
|
||||
@Override
|
||||
public String[] setPackagesSuspendedAsUser(String[] packageNames, boolean suspended,
|
||||
PersistableBundle appExtras, PersistableBundle launcherExtras, String dialogMessage,
|
||||
String callingPackage, int userId) {
|
||||
PersistableBundle appExtras, PersistableBundle launcherExtras,
|
||||
SuspendDialogInfo dialogInfo, String callingPackage, int userId) {
|
||||
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.SUSPEND_APPS,
|
||||
"setPackagesSuspendedAsUser");
|
||||
|
||||
@@ -12762,7 +12763,7 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
unactionedPackages.add(packageName);
|
||||
continue;
|
||||
}
|
||||
pkgSetting.setSuspended(suspended, callingPackage, dialogMessage, appExtras,
|
||||
pkgSetting.setSuspended(suspended, callingPackage, dialogInfo, appExtras,
|
||||
launcherExtras, userId);
|
||||
changedPackagesList.add(packageName);
|
||||
changedUids.add(UserHandle.getUid(userId, pkgSetting.appId));
|
||||
@@ -17816,7 +17817,7 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
false /*hidden*/,
|
||||
false /*suspended*/,
|
||||
null /*suspendingPackage*/,
|
||||
null /*dialogMessage*/,
|
||||
null /*dialogInfo*/,
|
||||
null /*suspendedAppExtras*/,
|
||||
null /*suspendedLauncherExtras*/,
|
||||
false /*instantApp*/,
|
||||
@@ -22585,10 +22586,10 @@ public class PackageManagerService extends IPackageManager.Stub
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSuspendedDialogMessage(String suspendedPackage, int userId) {
|
||||
public SuspendDialogInfo getSuspendedDialogInfo(String suspendedPackage, int userId) {
|
||||
synchronized (mPackages) {
|
||||
final PackageSetting ps = mSettings.mPackages.get(suspendedPackage);
|
||||
return (ps != null) ? ps.readUserState(userId).dialogMessage : null;
|
||||
return (ps != null) ? ps.readUserState(userId).dialogInfo : null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -51,6 +51,7 @@ import android.content.pm.ParceledListSlice;
|
||||
import android.content.pm.PermissionGroupInfo;
|
||||
import android.content.pm.PermissionInfo;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.content.pm.SuspendDialogInfo;
|
||||
import android.content.pm.UserInfo;
|
||||
import android.content.pm.VersionedPackage;
|
||||
import android.content.pm.dex.ArtManager;
|
||||
@@ -1701,9 +1702,18 @@ class PackageManagerShellCommand extends ShellCommand {
|
||||
}
|
||||
final String callingPackage =
|
||||
(Binder.getCallingUid() == Process.ROOT_UID) ? "root" : "com.android.shell";
|
||||
|
||||
final SuspendDialogInfo info;
|
||||
if (!TextUtils.isEmpty(dialogMessage)) {
|
||||
info = new SuspendDialogInfo.Builder()
|
||||
.setMessage(dialogMessage)
|
||||
.build();
|
||||
} else {
|
||||
info = null;
|
||||
}
|
||||
try {
|
||||
mInterface.setPackagesSuspendedAsUser(new String[]{packageName}, suspendedState,
|
||||
appExtras, launcherExtras, dialogMessage, callingPackage, userId);
|
||||
appExtras, launcherExtras, info, callingPackage, userId);
|
||||
pw.println("Package " + packageName + " new suspended state: "
|
||||
+ mInterface.isPackageSuspendedForUser(packageName, userId));
|
||||
return 0;
|
||||
|
||||
@@ -26,6 +26,7 @@ import android.content.pm.PackageManager;
|
||||
import android.content.pm.PackageParser;
|
||||
import android.content.pm.PackageUserState;
|
||||
import android.content.pm.Signature;
|
||||
import android.content.pm.SuspendDialogInfo;
|
||||
import android.os.PersistableBundle;
|
||||
import android.service.pm.PackageProto;
|
||||
import android.util.ArraySet;
|
||||
@@ -395,12 +396,12 @@ public abstract class PackageSettingBase extends SettingBase {
|
||||
return readUserState(userId).suspended;
|
||||
}
|
||||
|
||||
void setSuspended(boolean suspended, String suspendingPackage, String dialogMessage,
|
||||
void setSuspended(boolean suspended, String suspendingPackage, SuspendDialogInfo dialogInfo,
|
||||
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.dialogInfo = suspended ? dialogInfo : null;
|
||||
existingUserState.suspendedAppExtras = suspended ? appExtras : null;
|
||||
existingUserState.suspendedLauncherExtras = suspended ? launcherExtras : null;
|
||||
}
|
||||
@@ -423,7 +424,7 @@ 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,
|
||||
String dialogMessage, PersistableBundle suspendedAppExtras,
|
||||
SuspendDialogInfo dialogInfo, PersistableBundle suspendedAppExtras,
|
||||
PersistableBundle suspendedLauncherExtras, boolean instantApp,
|
||||
boolean virtualPreload, String lastDisableAppCaller,
|
||||
ArraySet<String> enabledComponents, ArraySet<String> disabledComponents,
|
||||
@@ -438,7 +439,7 @@ public abstract class PackageSettingBase extends SettingBase {
|
||||
state.hidden = hidden;
|
||||
state.suspended = suspended;
|
||||
state.suspendingPackage = suspendingPackage;
|
||||
state.dialogMessage = dialogMessage;
|
||||
state.dialogInfo = dialogInfo;
|
||||
state.suspendedAppExtras = suspendedAppExtras;
|
||||
state.suspendedLauncherExtras = suspendedLauncherExtras;
|
||||
state.lastDisableAppCaller = lastDisableAppCaller;
|
||||
|
||||
@@ -49,6 +49,7 @@ import android.content.pm.PackageUserState;
|
||||
import android.content.pm.PermissionInfo;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.content.pm.Signature;
|
||||
import android.content.pm.SuspendDialogInfo;
|
||||
import android.content.pm.UserInfo;
|
||||
import android.content.pm.VerifierDeviceIdentity;
|
||||
import android.net.Uri;
|
||||
@@ -203,6 +204,7 @@ public final class Settings {
|
||||
private static final String TAG_DEFAULT_BROWSER = "default-browser";
|
||||
private static final String TAG_DEFAULT_DIALER = "default-dialer";
|
||||
private static final String TAG_VERSION = "version";
|
||||
private static final String TAG_SUSPENDED_DIALOG_INFO = "suspended-dialog-info";
|
||||
private static final String TAG_SUSPENDED_APP_EXTRAS = "suspended-app-extras";
|
||||
private static final String TAG_SUSPENDED_LAUNCHER_EXTRAS = "suspended-launcher-extras";
|
||||
|
||||
@@ -222,6 +224,10 @@ 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";
|
||||
/**
|
||||
* @deprecated Legacy attribute, kept only for upgrading from P builds.
|
||||
*/
|
||||
@Deprecated
|
||||
private static final String ATTR_SUSPEND_DIALOG_MESSAGE = "suspend_dialog_message";
|
||||
// Legacy, uninstall blocks are stored separately.
|
||||
@Deprecated
|
||||
@@ -730,7 +736,7 @@ public final class Settings {
|
||||
false /*hidden*/,
|
||||
false /*suspended*/,
|
||||
null /*suspendingPackage*/,
|
||||
null /*dialogMessage*/,
|
||||
null /*dialogInfo*/,
|
||||
null /*suspendedAppExtras*/,
|
||||
null /*suspendedLauncherExtras*/,
|
||||
instantApp,
|
||||
@@ -1620,7 +1626,7 @@ public final class Settings {
|
||||
false /*hidden*/,
|
||||
false /*suspended*/,
|
||||
null /*suspendingPackage*/,
|
||||
null /*dialogMessage*/,
|
||||
null /*dialogInfo*/,
|
||||
null /*suspendedAppExtras*/,
|
||||
null /*suspendedLauncherExtras*/,
|
||||
false /*instantApp*/,
|
||||
@@ -1730,6 +1736,7 @@ public final class Settings {
|
||||
ArraySet<String> disabledComponents = null;
|
||||
PersistableBundle suspendedAppExtras = null;
|
||||
PersistableBundle suspendedLauncherExtras = null;
|
||||
SuspendDialogInfo suspendDialogInfo = null;
|
||||
|
||||
int packageDepth = parser.getDepth();
|
||||
while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
|
||||
@@ -1752,20 +1759,28 @@ public final class Settings {
|
||||
case TAG_SUSPENDED_LAUNCHER_EXTRAS:
|
||||
suspendedLauncherExtras = PersistableBundle.restoreFromXml(parser);
|
||||
break;
|
||||
case TAG_SUSPENDED_DIALOG_INFO:
|
||||
suspendDialogInfo = SuspendDialogInfo.restoreFromXml(parser);
|
||||
break;
|
||||
default:
|
||||
Slog.wtf(TAG, "Unknown tag " + parser.getName() + " under tag "
|
||||
+ TAG_PACKAGE);
|
||||
}
|
||||
}
|
||||
if (suspendDialogInfo == null && !TextUtils.isEmpty(dialogMessage)) {
|
||||
suspendDialogInfo = new SuspendDialogInfo.Builder()
|
||||
.setMessage(dialogMessage)
|
||||
.build();
|
||||
}
|
||||
|
||||
if (blockUninstall) {
|
||||
setBlockUninstallLPw(userId, name, true);
|
||||
}
|
||||
ps.setUserState(userId, ceDataInode, enabled, installed, stopped, notLaunched,
|
||||
hidden, suspended, suspendingPackage, dialogMessage, suspendedAppExtras,
|
||||
suspendedLauncherExtras, instantApp, virtualPreload, enabledCaller,
|
||||
enabledComponents, disabledComponents, verifState, linkGeneration,
|
||||
installReason, harmfulAppWarning);
|
||||
hidden, suspended, suspendingPackage, suspendDialogInfo,
|
||||
suspendedAppExtras, suspendedLauncherExtras, instantApp, virtualPreload,
|
||||
enabledCaller, enabledComponents, disabledComponents, verifState,
|
||||
linkGeneration, installReason, harmfulAppWarning);
|
||||
} else if (tagName.equals("preferred-activities")) {
|
||||
readPreferredActivitiesLPw(parser, userId);
|
||||
} else if (tagName.equals(TAG_PERSISTENT_PREFERRED_ACTIVITIES)) {
|
||||
@@ -2076,9 +2091,10 @@ public final class Settings {
|
||||
serializer.attribute(null, ATTR_SUSPENDING_PACKAGE,
|
||||
ustate.suspendingPackage);
|
||||
}
|
||||
if (ustate.dialogMessage != null) {
|
||||
serializer.attribute(null, ATTR_SUSPEND_DIALOG_MESSAGE,
|
||||
ustate.dialogMessage);
|
||||
if (ustate.dialogInfo != null) {
|
||||
serializer.startTag(null, TAG_SUSPENDED_DIALOG_INFO);
|
||||
ustate.dialogInfo.saveToXml(serializer);
|
||||
serializer.endTag(null, TAG_SUSPENDED_DIALOG_INFO);
|
||||
}
|
||||
if (ustate.suspendedAppExtras != null) {
|
||||
serializer.startTag(null, TAG_SUSPENDED_APP_EXTRAS);
|
||||
@@ -4737,8 +4753,8 @@ public final class Settings {
|
||||
final PackageUserState pus = ps.readUserState(user.id);
|
||||
pw.print(" suspendingPackage=");
|
||||
pw.print(pus.suspendingPackage);
|
||||
pw.print(" dialogMessage=");
|
||||
pw.print(pus.dialogMessage);
|
||||
pw.print(" dialogInfo=");
|
||||
pw.print(pus.dialogInfo);
|
||||
}
|
||||
pw.print(" stopped=");
|
||||
pw.print(ps.getStopped(user.id));
|
||||
|
||||
@@ -35,6 +35,7 @@ import android.content.Intent;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManagerInternal;
|
||||
import android.content.pm.SuspendDialogInfo;
|
||||
import android.content.pm.UserInfo;
|
||||
import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
@@ -165,17 +166,20 @@ public class ActivityStartInterceptorTest {
|
||||
public void testSuspendedPackage() {
|
||||
mAInfo.applicationInfo.flags = FLAG_SUSPENDED;
|
||||
final String suspendingPackage = "com.test.suspending.package";
|
||||
final String dialogMessage = "Test Message";
|
||||
final SuspendDialogInfo dialogInfo = new SuspendDialogInfo.Builder()
|
||||
.setMessage("Test Message")
|
||||
.setIcon(0x11110001)
|
||||
.build();
|
||||
when(mPackageManagerInternal.getSuspendingPackage(TEST_PACKAGE_NAME, TEST_USER_ID))
|
||||
.thenReturn(suspendingPackage);
|
||||
when(mPackageManagerInternal.getSuspendedDialogMessage(TEST_PACKAGE_NAME, TEST_USER_ID))
|
||||
.thenReturn(dialogMessage);
|
||||
when(mPackageManagerInternal.getSuspendedDialogInfo(TEST_PACKAGE_NAME, TEST_USER_ID))
|
||||
.thenReturn(dialogInfo);
|
||||
// THEN calling intercept returns true
|
||||
assertTrue(mInterceptor.intercept(null, null, mAInfo, null, null, 0, 0, null));
|
||||
|
||||
// Check intent parameters
|
||||
assertEquals(dialogMessage,
|
||||
mInterceptor.mIntent.getStringExtra(SuspendedAppActivity.EXTRA_DIALOG_MESSAGE));
|
||||
assertEquals(dialogInfo,
|
||||
mInterceptor.mIntent.getParcelableExtra(SuspendedAppActivity.EXTRA_DIALOG_INFO));
|
||||
assertEquals(suspendingPackage,
|
||||
mInterceptor.mIntent.getStringExtra(SuspendedAppActivity.EXTRA_SUSPENDING_PACKAGE));
|
||||
assertEquals(TEST_PACKAGE_NAME,
|
||||
|
||||
@@ -37,6 +37,7 @@ import android.content.Context;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageParser;
|
||||
import android.content.pm.PackageUserState;
|
||||
import android.content.pm.SuspendDialogInfo;
|
||||
import android.content.pm.UserInfo;
|
||||
import android.os.BaseBundle;
|
||||
import android.os.PersistableBundle;
|
||||
@@ -200,13 +201,21 @@ 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", "dialogMsg1", appExtras1, launcherExtras1, 0);
|
||||
|
||||
final SuspendDialogInfo dialogInfo1 = new SuspendDialogInfo.Builder()
|
||||
.setIcon(0x11220001)
|
||||
.setTitle(0x11220002)
|
||||
.setMessage("1st message")
|
||||
.setNeutralButtonText(0x11220003)
|
||||
.build();
|
||||
|
||||
ps1.setSuspended(true, "suspendingPackage1", dialogInfo1, appExtras1, launcherExtras1, 0);
|
||||
settingsUnderTest.mPackages.put(PACKAGE_NAME_1, ps1);
|
||||
|
||||
ps2.setSuspended(true, "suspendingPackage2", "dialogMsg2", null, null, 0);
|
||||
ps2.setSuspended(true, "suspendingPackage2", null, null, null, 0);
|
||||
settingsUnderTest.mPackages.put(PACKAGE_NAME_2, ps2);
|
||||
|
||||
ps3.setSuspended(false, "irrelevant", "irrevelant2", null, null, 0);
|
||||
ps3.setSuspended(false, "irrelevant", dialogInfo1, null, null, 0);
|
||||
settingsUnderTest.mPackages.put(PACKAGE_NAME_3, ps3);
|
||||
|
||||
settingsUnderTest.writePackageRestrictionsLPr(0);
|
||||
@@ -221,7 +230,7 @@ public class PackageManagerSettingsTests {
|
||||
readUserState(0);
|
||||
assertThat(readPus1.suspended, is(true));
|
||||
assertThat(readPus1.suspendingPackage, equalTo("suspendingPackage1"));
|
||||
assertThat(readPus1.dialogMessage, equalTo("dialogMsg1"));
|
||||
assertThat(readPus1.dialogInfo, equalTo(dialogInfo1));
|
||||
assertThat(BaseBundle.kindofEquals(readPus1.suspendedAppExtras, appExtras1), is(true));
|
||||
assertThat(BaseBundle.kindofEquals(readPus1.suspendedLauncherExtras, launcherExtras1),
|
||||
is(true));
|
||||
@@ -230,7 +239,7 @@ public class PackageManagerSettingsTests {
|
||||
readUserState(0);
|
||||
assertThat(readPus2.suspended, is(true));
|
||||
assertThat(readPus2.suspendingPackage, equalTo("suspendingPackage2"));
|
||||
assertThat(readPus2.dialogMessage, equalTo("dialogMsg2"));
|
||||
assertThat(readPus2.dialogInfo, is(nullValue()));
|
||||
assertThat(readPus2.suspendedAppExtras, is(nullValue()));
|
||||
assertThat(readPus2.suspendedLauncherExtras, is(nullValue()));
|
||||
|
||||
@@ -238,7 +247,7 @@ public class PackageManagerSettingsTests {
|
||||
readUserState(0);
|
||||
assertThat(readPus3.suspended, is(false));
|
||||
assertThat(readPus3.suspendingPackage, is(nullValue()));
|
||||
assertThat(readPus3.dialogMessage, is(nullValue()));
|
||||
assertThat(readPus3.dialogInfo, is(nullValue()));
|
||||
assertThat(readPus3.suspendedAppExtras, is(nullValue()));
|
||||
assertThat(readPus3.suspendedLauncherExtras, is(nullValue()));
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import android.content.pm.PackageUserState;
|
||||
import android.content.pm.SuspendDialogInfo;
|
||||
import android.os.PersistableBundle;
|
||||
import android.util.ArraySet;
|
||||
|
||||
@@ -37,7 +38,7 @@ import org.junit.runner.RunWith;
|
||||
public class PackageUserStateTest {
|
||||
|
||||
@Test
|
||||
public void testPackageUserState01() {
|
||||
public void testPackageUserState01() {
|
||||
final PackageUserState testUserState = new PackageUserState();
|
||||
PackageUserState oldUserState;
|
||||
|
||||
@@ -84,7 +85,7 @@ public class PackageUserStateTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPackageUserState02() {
|
||||
public void testPackageUserState02() {
|
||||
final PackageUserState testUserState01 = new PackageUserState();
|
||||
PackageUserState oldUserState;
|
||||
|
||||
@@ -102,7 +103,7 @@ public class PackageUserStateTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPackageUserState03() {
|
||||
public void testPackageUserState03() {
|
||||
final PackageUserState oldUserState = new PackageUserState();
|
||||
|
||||
// only new user state has array defined; different
|
||||
@@ -138,7 +139,7 @@ public class PackageUserStateTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPackageUserState04() {
|
||||
public void testPackageUserState04() {
|
||||
final PackageUserState oldUserState = new PackageUserState();
|
||||
|
||||
// only new user state has array defined; different
|
||||
@@ -185,15 +186,19 @@ public class PackageUserStateTest {
|
||||
launcherExtras2.putString("name", "launcherExtras2");
|
||||
final String suspendingPackage1 = "package1";
|
||||
final String suspendingPackage2 = "package2";
|
||||
final String dialogMessage1 = "dialogMessage1";
|
||||
final String dialogMessage2 = "dialogMessage2";
|
||||
final SuspendDialogInfo dialogInfo1 = new SuspendDialogInfo.Builder()
|
||||
.setMessage("dialogMessage1")
|
||||
.build();
|
||||
final SuspendDialogInfo dialogInfo2 = new SuspendDialogInfo.Builder()
|
||||
.setMessage("dialogMessage2")
|
||||
.build();
|
||||
|
||||
final PackageUserState testUserState1 = new PackageUserState();
|
||||
testUserState1.suspended = true;
|
||||
testUserState1.suspendedAppExtras = appExtras1;
|
||||
testUserState1.suspendedLauncherExtras = launcherExtras1;
|
||||
testUserState1.suspendingPackage = suspendingPackage1;
|
||||
testUserState1.dialogMessage = dialogMessage1;
|
||||
testUserState1.dialogInfo = dialogInfo1;
|
||||
|
||||
PackageUserState testUserState2 = new PackageUserState(testUserState1);
|
||||
assertThat(testUserState1.equals(testUserState2), is(true));
|
||||
@@ -209,14 +214,14 @@ public class PackageUserStateTest {
|
||||
assertThat(testUserState1.equals(testUserState2), is(false));
|
||||
|
||||
testUserState2 = new PackageUserState(testUserState1);
|
||||
testUserState2.dialogMessage = dialogMessage2;
|
||||
testUserState2.dialogInfo = dialogInfo2;
|
||||
assertThat(testUserState1.equals(testUserState2), is(false));
|
||||
|
||||
testUserState2 = new PackageUserState(testUserState1);
|
||||
testUserState2.suspended = testUserState1.suspended = false;
|
||||
// Everything is different but irrelevant if suspended is false
|
||||
testUserState2.suspendingPackage = suspendingPackage2;
|
||||
testUserState2.dialogMessage = dialogMessage2;
|
||||
testUserState2.dialogInfo = dialogInfo2;
|
||||
testUserState2.suspendedAppExtras = appExtras2;
|
||||
testUserState2.suspendedLauncherExtras = launcherExtras2;
|
||||
assertThat(testUserState1.equals(testUserState2), is(true));
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* 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 org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
|
||||
import android.content.pm.SuspendDialogInfo;
|
||||
|
||||
import androidx.test.filters.SmallTest;
|
||||
import androidx.test.runner.AndroidJUnit4;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@SmallTest
|
||||
public class SuspendDialogInfoTest {
|
||||
private static final int VALID_TEST_RES_ID_1 = 0x11110001;
|
||||
private static final int VALID_TEST_RES_ID_2 = 0x11110002;
|
||||
|
||||
private static SuspendDialogInfo.Builder createDefaultDialogBuilder() {
|
||||
return new SuspendDialogInfo.Builder()
|
||||
.setIcon(VALID_TEST_RES_ID_1)
|
||||
.setTitle(VALID_TEST_RES_ID_1)
|
||||
.setMessage(VALID_TEST_RES_ID_1)
|
||||
.setNeutralButtonText(VALID_TEST_RES_ID_1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void equalsComparesIcons() {
|
||||
final SuspendDialogInfo.Builder dialogBuilder1 = createDefaultDialogBuilder();
|
||||
final SuspendDialogInfo.Builder dialogBuilder2 = createDefaultDialogBuilder();
|
||||
assertEquals(dialogBuilder1.build(), dialogBuilder2.build());
|
||||
// Only icon is different
|
||||
dialogBuilder2.setIcon(VALID_TEST_RES_ID_2);
|
||||
assertNotEquals(dialogBuilder1.build(), dialogBuilder2.build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void equalsComparesTitle() {
|
||||
final SuspendDialogInfo.Builder dialogBuilder1 = createDefaultDialogBuilder();
|
||||
final SuspendDialogInfo.Builder dialogBuilder2 = createDefaultDialogBuilder();
|
||||
assertEquals(dialogBuilder1.build(), dialogBuilder2.build());
|
||||
// Only title is different
|
||||
dialogBuilder2.setTitle(VALID_TEST_RES_ID_2);
|
||||
assertNotEquals(dialogBuilder1.build(), dialogBuilder2.build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void equalsComparesButtonText() {
|
||||
final SuspendDialogInfo.Builder dialogBuilder1 = createDefaultDialogBuilder();
|
||||
final SuspendDialogInfo.Builder dialogBuilder2 = createDefaultDialogBuilder();
|
||||
assertEquals(dialogBuilder1.build(), dialogBuilder2.build());
|
||||
// Only button text is different
|
||||
dialogBuilder2.setNeutralButtonText(VALID_TEST_RES_ID_2);
|
||||
assertNotEquals(dialogBuilder1.build(), dialogBuilder2.build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void equalsComparesMessageIds() {
|
||||
final SuspendDialogInfo.Builder dialogBuilder1 = createDefaultDialogBuilder();
|
||||
final SuspendDialogInfo.Builder dialogBuilder2 = createDefaultDialogBuilder();
|
||||
assertEquals(dialogBuilder1.build(), dialogBuilder2.build());
|
||||
// Only message is different
|
||||
dialogBuilder2.setMessage(VALID_TEST_RES_ID_2);
|
||||
assertNotEquals(dialogBuilder1.build(), dialogBuilder2.build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void equalsIgnoresMessageStringsWhenIdsSet() {
|
||||
final SuspendDialogInfo.Builder dialogBuilder1 = new SuspendDialogInfo.Builder()
|
||||
.setMessage(VALID_TEST_RES_ID_1)
|
||||
.setMessage("1st message");
|
||||
final SuspendDialogInfo.Builder dialogBuilder2 = new SuspendDialogInfo.Builder()
|
||||
.setMessage(VALID_TEST_RES_ID_1)
|
||||
.setMessage("2nd message");
|
||||
// String messages different but should get be ignored when resource ids are set
|
||||
assertEquals(dialogBuilder1.build(), dialogBuilder2.build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void equalsComparesMessageStringsWhenNoIdsSet() {
|
||||
final SuspendDialogInfo.Builder dialogBuilder1 = new SuspendDialogInfo.Builder()
|
||||
.setMessage("1st message");
|
||||
final SuspendDialogInfo.Builder dialogBuilder2 = new SuspendDialogInfo.Builder()
|
||||
.setMessage("2nd message");
|
||||
// Both have different messages, which are not ignored as resource ids aren't set
|
||||
assertNotEquals(dialogBuilder1.build(), dialogBuilder2.build());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void messageStringClearedWhenResIdSet() {
|
||||
final SuspendDialogInfo dialogInfo = new SuspendDialogInfo.Builder()
|
||||
.setMessage(VALID_TEST_RES_ID_2)
|
||||
.setMessage("Should be cleared on build")
|
||||
.build();
|
||||
assertNull(dialogInfo.getDialogMessage());
|
||||
assertEquals(VALID_TEST_RES_ID_2, dialogInfo.getDialogMessageResId());
|
||||
}
|
||||
}
|
||||
@@ -33,6 +33,7 @@ import android.content.IntentFilter;
|
||||
import android.content.pm.IPackageManager;
|
||||
import android.content.pm.LauncherApps;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.SuspendDialogInfo;
|
||||
import android.content.res.Resources;
|
||||
import android.os.BaseBundle;
|
||||
import android.os.Bundle;
|
||||
@@ -152,7 +153,7 @@ public class SuspendPackagesTest {
|
||||
}
|
||||
|
||||
void drainPendingBroadcasts() {
|
||||
while (pollForIntent(5) != null);
|
||||
while (pollForIntent(5) != null) ;
|
||||
}
|
||||
|
||||
Intent receiveIntentFromApp() {
|
||||
@@ -215,15 +216,15 @@ public class SuspendPackagesTest {
|
||||
}
|
||||
|
||||
private void suspendTestPackage(PersistableBundle appExtras, PersistableBundle launcherExtras,
|
||||
String dialogMessage) {
|
||||
SuspendDialogInfo dialogInfo) {
|
||||
final String[] unchangedPackages = mPackageManager.setPackagesSuspended(
|
||||
PACKAGES_TO_SUSPEND, true, appExtras, launcherExtras, dialogMessage);
|
||||
PACKAGES_TO_SUSPEND, true, appExtras, launcherExtras, dialogInfo);
|
||||
assertTrue("setPackagesSuspended returned non-empty list", unchangedPackages.length == 0);
|
||||
}
|
||||
|
||||
private void unsuspendTestPackage() {
|
||||
final String[] unchangedPackages = mPackageManager.setPackagesSuspended(
|
||||
PACKAGES_TO_SUSPEND, false, null, null, null);
|
||||
PACKAGES_TO_SUSPEND, false, null, null, (SuspendDialogInfo) null);
|
||||
assertTrue("setPackagesSuspended returned non-empty list", unchangedPackages.length == 0);
|
||||
}
|
||||
|
||||
@@ -318,7 +319,8 @@ public class SuspendPackagesTest {
|
||||
@Test
|
||||
public void testCannotSuspendSelf() {
|
||||
final String[] unchangedPkgs = mPackageManager.setPackagesSuspended(
|
||||
new String[]{mContext.getOpPackageName()}, true, null, null, null);
|
||||
new String[]{mContext.getOpPackageName()}, true, null, null,
|
||||
(SuspendDialogInfo) null);
|
||||
assertTrue(unchangedPkgs.length == 1);
|
||||
assertEquals(mContext.getOpPackageName(), unchangedPkgs[0]);
|
||||
}
|
||||
@@ -457,7 +459,8 @@ public class SuspendPackagesTest {
|
||||
mAppCommsReceiver.register(mReceiverHandler, ACTION_REPORT_MORE_DETAILS_ACTIVITY_STARTED,
|
||||
ACTION_REPORT_TEST_ACTIVITY_STARTED);
|
||||
final String testMessage = "This is a test message to report suspension of %1$s";
|
||||
suspendTestPackage(null, null, testMessage);
|
||||
suspendTestPackage(null, null,
|
||||
new SuspendDialogInfo.Builder().setMessage(testMessage).build());
|
||||
startTestAppActivity();
|
||||
assertNull("No broadcast was expected from app", mAppCommsReceiver.pollForIntent(2));
|
||||
assertNotNull("Given dialog message not shown", mUiDevice.wait(
|
||||
|
||||
@@ -29,6 +29,7 @@ package android.test.mock {
|
||||
method public void removeOnPermissionsChangeListener(android.content.pm.PackageManager.OnPermissionsChangedListener);
|
||||
method public void revokeRuntimePermission(java.lang.String, java.lang.String, android.os.UserHandle);
|
||||
method public boolean setDefaultBrowserPackageNameAsUser(java.lang.String, int);
|
||||
method public java.lang.String[] setPackagesSuspended(java.lang.String[], boolean, android.os.PersistableBundle, android.os.PersistableBundle, java.lang.String);
|
||||
method public void setUpdateAvailable(java.lang.String, boolean);
|
||||
method public boolean updateIntentVerificationStatusAsUser(java.lang.String, int, int);
|
||||
method public void updatePermissionFlags(java.lang.String, java.lang.String, int, int, android.os.UserHandle);
|
||||
|
||||
Reference in New Issue
Block a user