Implement harmful app warning at activity launch
Bug: 63909431
Test: manual
Change-Id: I8a5497421cb8130af8cdd5129b0f6e1707a01e36
diff --git a/api/system-current.txt b/api/system-current.txt
index b2d5a49..ee6622e 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -153,6 +153,7 @@
field public static final java.lang.String SET_ALWAYS_FINISH = "android.permission.SET_ALWAYS_FINISH";
field public static final java.lang.String SET_ANIMATION_SCALE = "android.permission.SET_ANIMATION_SCALE";
field public static final java.lang.String SET_DEBUG_APP = "android.permission.SET_DEBUG_APP";
+ field public static final java.lang.String SET_HARMFUL_APP_WARNINGS = "android.permission.SET_HARMFUL_APP_WARNINGS";
field public static final java.lang.String SET_MEDIA_KEY_LISTENER = "android.permission.SET_MEDIA_KEY_LISTENER";
field public static final java.lang.String SET_ORIENTATION = "android.permission.SET_ORIENTATION";
field public static final java.lang.String SET_POINTER_SPEED = "android.permission.SET_POINTER_SPEED";
@@ -896,6 +897,7 @@
method public abstract java.util.List<android.content.IntentFilter> getAllIntentFilters(java.lang.String);
method public android.content.pm.dex.ArtManager getArtManager();
method public abstract java.lang.String getDefaultBrowserPackageNameAsUser(int);
+ method public java.lang.CharSequence getHarmfulAppWarning(java.lang.String);
method public abstract java.util.List<android.content.pm.PackageInfo> getInstalledPackagesAsUser(int, int);
method public abstract android.graphics.drawable.Drawable getInstantAppIcon(java.lang.String);
method public abstract android.content.ComponentName getInstantAppInstallerComponent();
@@ -912,6 +914,7 @@
method public abstract void removeOnPermissionsChangeListener(android.content.pm.PackageManager.OnPermissionsChangedListener);
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 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);
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 82f2bac..4048e65 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -2738,6 +2738,24 @@
}
@Override
+ public CharSequence getHarmfulAppWarning(String packageName) {
+ try {
+ return mPM.getHarmfulAppWarning(packageName, mContext.getUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ @Override
+ public void setHarmfulAppWarning(String packageName, CharSequence warning) {
+ try {
+ mPM.setHarmfulAppWarning(packageName, warning, mContext.getUserId());
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ @Override
public ArtManager getArtManager() {
synchronized (mLock) {
if (mArtManager == null) {
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index e319750..cce6b84 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -652,4 +652,8 @@
String getInstantAppAndroidId(String packageName, int userId);
IArtManager getArtManager();
+
+ void setHarmfulAppWarning(String packageName, CharSequence warning, int userId);
+
+ CharSequence getHarmfulAppWarning(String packageName, int userId);
}
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 6cd4285..8a6484c 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -5862,4 +5862,37 @@
public @NonNull ArtManager getArtManager() {
throw new UnsupportedOperationException("getArtManager not implemented in subclass");
}
+
+ /**
+ * Sets or clears the harmful app warning details for the given app.
+ *
+ * When set, any attempt to launch an activity in this package will be intercepted and a
+ * warning dialog will be shown to the user instead, with the given warning. The user
+ * will have the option to proceed with the activity launch, or to uninstall the application.
+ *
+ * @param packageName The full name of the package to warn on.
+ * @param warning A warning string to display to the user describing the threat posed by the
+ * application, or null to clear the warning.
+ *
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.SET_HARMFUL_APP_WARNINGS)
+ @SystemApi
+ public void setHarmfulAppWarning(@NonNull String packageName, @Nullable CharSequence warning) {
+ throw new UnsupportedOperationException("setHarmfulAppWarning not implemented in subclass");
+ }
+
+ /**
+ * Returns the harmful app warning string for the given app, or null if there is none set.
+ *
+ * @param packageName The full name of the desired package.
+ *
+ * @hide
+ */
+ @RequiresPermission(Manifest.permission.SET_HARMFUL_APP_WARNINGS)
+ @Nullable
+ @SystemApi
+ public CharSequence getHarmfulAppWarning(@NonNull String packageName) {
+ throw new UnsupportedOperationException("getHarmfulAppWarning not implemented in subclass");
+ }
}
diff --git a/core/java/android/content/pm/PackageUserState.java b/core/java/android/content/pm/PackageUserState.java
index 069b2d4..293beb2 100644
--- a/core/java/android/content/pm/PackageUserState.java
+++ b/core/java/android/content/pm/PackageUserState.java
@@ -52,6 +52,7 @@
public int appLinkGeneration;
public int categoryHint = ApplicationInfo.CATEGORY_UNDEFINED;
public int installReason;
+ public String harmfulAppWarning;
public ArraySet<String> disabledComponents;
public ArraySet<String> enabledComponents;
@@ -87,6 +88,7 @@
enabledComponents = ArrayUtils.cloneOrNull(o.enabledComponents);
overlayPaths =
o.overlayPaths == null ? null : Arrays.copyOf(o.overlayPaths, o.overlayPaths.length);
+ harmfulAppWarning = o.harmfulAppWarning;
}
/**
@@ -247,6 +249,11 @@
}
}
}
+ if (harmfulAppWarning == null && oldState.harmfulAppWarning != null
+ || (harmfulAppWarning != null
+ && !harmfulAppWarning.equals(oldState.harmfulAppWarning))) {
+ return false;
+ }
return true;
}
}
diff --git a/core/java/com/android/internal/app/HarmfulAppWarningActivity.java b/core/java/com/android/internal/app/HarmfulAppWarningActivity.java
new file mode 100644
index 0000000..042da36
--- /dev/null
+++ b/core/java/com/android/internal/app/HarmfulAppWarningActivity.java
@@ -0,0 +1,99 @@
+/*
+ * 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.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.IntentSender;
+import android.os.Bundle;
+import android.util.Log;
+import com.android.internal.R;
+
+/**
+ * This dialog is shown to the user before an activity in a harmful app is launched.
+ *
+ * See {@code PackageManager.setHarmfulAppInfo} for more info.
+ */
+public class HarmfulAppWarningActivity extends AlertActivity implements
+ DialogInterface.OnClickListener {
+ private static final String TAG = "HarmfulAppWarningActivity";
+
+ private static final String EXTRA_HARMFUL_APP_WARNING = "harmful_app_warning";
+
+ private String mPackageName;
+ private String mHarmfulAppWarning;
+ private IntentSender mTarget;
+
+ // [b/63909431] STOPSHIP replace placeholder UI with final Harmful App Warning UI
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ Intent intent = getIntent();
+ mPackageName = intent.getStringExtra(Intent.EXTRA_PACKAGE_NAME);
+ mTarget = intent.getParcelableExtra(Intent.EXTRA_INTENT);
+ mHarmfulAppWarning = intent.getStringExtra(EXTRA_HARMFUL_APP_WARNING);
+
+ if (mPackageName == null || mTarget == null || mHarmfulAppWarning == null) {
+ Log.wtf(TAG, "Invalid intent: " + intent.toString());
+ finish();
+ }
+
+ AlertController.AlertParams p = mAlertParams;
+ p.mTitle = getString(R.string.harmful_app_warning_title);
+ p.mMessage = mHarmfulAppWarning;
+ p.mPositiveButtonText = getString(R.string.harmful_app_warning_launch_anyway);
+ p.mPositiveButtonListener = this;
+ p.mNegativeButtonText = getString(R.string.harmful_app_warning_uninstall);
+ p.mNegativeButtonListener = this;
+
+ mAlert.installContent(mAlertParams);
+ }
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ switch (which) {
+ case DialogInterface.BUTTON_POSITIVE:
+ getPackageManager().setHarmfulAppWarning(mPackageName, null);
+
+ IntentSender target = getIntent().getParcelableExtra(Intent.EXTRA_INTENT);
+ try {
+ startIntentSenderForResult(target, -1, null, 0, 0, 0);
+ } catch (IntentSender.SendIntentException e) {
+ // ignore..
+ }
+ finish();
+ break;
+ case DialogInterface.BUTTON_NEGATIVE:
+ getPackageManager().deletePackage(mPackageName, null, 0);
+ finish();
+ break;
+ }
+ }
+
+ public static Intent createHarmfulAppWarningIntent(Context context, String targetPackageName,
+ IntentSender target, CharSequence harmfulAppWarning) {
+ Intent intent = new Intent();
+ intent.setClass(context, HarmfulAppWarningActivity.class);
+ intent.putExtra(Intent.EXTRA_PACKAGE_NAME, targetPackageName);
+ intent.putExtra(Intent.EXTRA_INTENT, target);
+ intent.putExtra(EXTRA_HARMFUL_APP_WARNING, harmfulAppWarning);
+ return intent;
+ }
+}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 00e3a4a..aab7f65 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -3304,6 +3304,10 @@
<permission android:name="android.permission.BIND_PACKAGE_VERIFIER"
android:protectionLevel="signature" />
+ <!-- @SystemApi @hide Allows an application to mark other applications as harmful -->
+ <permission android:name="android.permission.SET_HARMFUL_APP_WARNINGS"
+ android:protectionLevel="signature|verifier" />
+
<!-- @SystemApi @hide Intent filter verifier needs to have this permission before the
PackageManager will trust it to verify intent filters.
-->
@@ -3869,6 +3873,13 @@
android:excludeFromRecents="true">
</activity>
+ <activity android:name="com.android.internal.app.HarmfulAppWarningActivity"
+ android:theme="@style/Theme.DeviceDefault.Light.Dialog.Alert"
+ android:excludeFromRecents="true"
+ android:process=":ui"
+ android:exported="false">
+ </activity>
+
<receiver android:name="com.android.server.BootReceiver"
android:systemUserOnly="true">
<intent-filter android:priority="1000">
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 0618a820..9900b16 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -4795,4 +4795,14 @@
<!--Battery saver warning. STOPSHIP: Remove it eventually. -->
<string name="battery_saver_warning_title" translatable="false">Extreme battery saver</string>
+
+
+ <!-- Label for the uninstall button on the harmful app warning dialog. -->
+ <string name="harmful_app_warning_uninstall">Uninstall</string>
+ <!-- Label for the launch anyway button on the harmful app warning dialog. -->
+ <string name="harmful_app_warning_launch_anyway">Launch anyway</string>
+ <!-- Title for the harmful app warning dialog. -->
+ <string name="harmful_app_warning_title">Uninstall harmful app?</string>
+
+
</resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 22a4251..2092f62 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3205,4 +3205,8 @@
<java-symbol type="array" name="config_screenBrightnessNits" />
<java-symbol type="string" name="shortcut_disabled_reason_unknown" />
+
+ <java-symbol type="string" name="harmful_app_warning_uninstall" />
+ <java-symbol type="string" name="harmful_app_warning_launch_anyway" />
+ <java-symbol type="string" name="harmful_app_warning_title" />
</resources>
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index b3d6357..0f43db0 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -134,6 +134,7 @@
<!-- Permission needed to access privileged VR APIs -->
<uses-permission android:name="android.permission.RESTRICTED_VR_ACCESS" />
<uses-permission android:name="android.permission.MANAGE_BIND_INSTANT_SERVICE" />
+ <uses-permission android:name="android.permission.SET_HARMFUL_APP_WARNINGS" />
<application android:label="@string/app_label"
android:defaultToDeviceProtectedStorage="true"
diff --git a/services/core/java/com/android/server/am/ActivityStartInterceptor.java b/services/core/java/com/android/server/am/ActivityStartInterceptor.java
index 6684f25..0480646 100644
--- a/services/core/java/com/android/server/am/ActivityStartInterceptor.java
+++ b/services/core/java/com/android/server/am/ActivityStartInterceptor.java
@@ -30,6 +30,7 @@
import static android.content.pm.ApplicationInfo.FLAG_SUSPENDED;
import android.app.ActivityOptions;
+import android.app.AppGlobals;
import android.app.KeyguardManager;
import android.app.admin.DevicePolicyManagerInternal;
import android.content.Context;
@@ -40,10 +41,12 @@
import android.content.pm.ResolveInfo;
import android.content.pm.UserInfo;
import android.os.Binder;
+import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.app.HarmfulAppWarningActivity;
import com.android.internal.app.UnlaunchableAppActivity;
import com.android.server.LocalServices;
@@ -115,6 +118,15 @@
mCallingPackage = callingPackage;
}
+ private IntentSender createIntentSenderForOriginalIntent(int callingUid, int flags) {
+ final IIntentSender target = mService.getIntentSenderLocked(
+ INTENT_SENDER_ACTIVITY, mCallingPackage, callingUid, mUserId, null /*token*/,
+ null /*resultCode*/, 0 /*requestCode*/,
+ new Intent[] { mIntent }, new String[] { mResolvedType },
+ flags, null /*bOptions*/);
+ return new IntentSender(target);
+ }
+
/**
* Intercept the launch intent based on various signals. If an interception happened the
* internal variables get assigned and need to be read explicitly by the caller.
@@ -144,6 +156,11 @@
// be unlocked when profile's user is running.
return true;
}
+ if (interceptHarmfulAppIfNeeded()) {
+ // If the app has a "harmful app" warning associated with it, we should ask to uninstall
+ // before issuing the work challenge.
+ return true;
+ }
return interceptWorkProfileChallengeIfNeeded();
}
@@ -152,13 +169,10 @@
if (!mUserManager.isQuietModeEnabled(UserHandle.of(mUserId))) {
return false;
}
- IIntentSender target = mService.getIntentSenderLocked(
- INTENT_SENDER_ACTIVITY, mCallingPackage, mCallingUid, mUserId, null, null, 0,
- new Intent[] {mIntent}, new String[] {mResolvedType},
- FLAG_CANCEL_CURRENT | FLAG_ONE_SHOT, null);
+ IntentSender target = createIntentSenderForOriginalIntent(mCallingUid,
+ FLAG_CANCEL_CURRENT | FLAG_ONE_SHOT);
- mIntent = UnlaunchableAppActivity.createInQuietModeDialogIntent(mUserId,
- new IntentSender(target));
+ mIntent = UnlaunchableAppActivity.createInQuietModeDialogIntent(mUserId, target);
mCallingPid = mRealCallingPid;
mCallingUid = mRealCallingUid;
mResolvedType = null;
@@ -240,11 +254,8 @@
return null;
}
// TODO(b/28935539): should allow certain activities to bypass work challenge
- final IIntentSender target = mService.getIntentSenderLocked(
- INTENT_SENDER_ACTIVITY, callingPackage,
- Binder.getCallingUid(), userId, null, null, 0, new Intent[]{ intent },
- new String[]{ resolvedType },
- FLAG_CANCEL_CURRENT | FLAG_ONE_SHOT | FLAG_IMMUTABLE, null);
+ final IntentSender target = createIntentSenderForOriginalIntent(Binder.getCallingUid(),
+ FLAG_CANCEL_CURRENT | FLAG_ONE_SHOT | FLAG_IMMUTABLE);
final KeyguardManager km = (KeyguardManager) mServiceContext
.getSystemService(KEYGUARD_SERVICE);
final Intent newIntent = km.createConfirmDeviceCredentialIntent(null, null, userId);
@@ -254,8 +265,36 @@
newIntent.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS |
FLAG_ACTIVITY_TASK_ON_HOME);
newIntent.putExtra(EXTRA_PACKAGE_NAME, aInfo.packageName);
- newIntent.putExtra(EXTRA_INTENT, new IntentSender(target));
+ newIntent.putExtra(EXTRA_INTENT, target);
return newIntent;
}
+ private boolean interceptHarmfulAppIfNeeded() {
+ CharSequence harmfulAppWarning;
+ try {
+ harmfulAppWarning = AppGlobals.getPackageManager().getHarmfulAppWarning(
+ mAInfo.packageName, mUserId);
+ } catch (RemoteException e) {
+ return false;
+ }
+
+ if (harmfulAppWarning == null) {
+ return false;
+ }
+
+ final IntentSender target = createIntentSenderForOriginalIntent(mCallingUid,
+ FLAG_CANCEL_CURRENT | FLAG_ONE_SHOT | FLAG_IMMUTABLE);
+
+ mIntent = HarmfulAppWarningActivity.createHarmfulAppWarningIntent(mServiceContext,
+ mAInfo.packageName, target, harmfulAppWarning);
+
+ mCallingPid = mRealCallingPid;
+ mCallingUid = mRealCallingUid;
+ mResolvedType = null;
+
+ mRInfo = mSupervisor.resolveIntent(mIntent, mResolvedType, mUserId);
+ mAInfo = mSupervisor.resolveActivity(mIntent, mRInfo, mStartFlags, null /*profilerInfo*/);
+ return true;
+ }
+
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 44aad44..1dbd9c0 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -17,6 +17,7 @@
package com.android.server.pm;
import static android.Manifest.permission.DELETE_PACKAGES;
+import static android.Manifest.permission.SET_HARMFUL_APP_WARNINGS;
import static android.Manifest.permission.INSTALL_PACKAGES;
import static android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS;
import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
@@ -167,7 +168,6 @@
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.pm.PackageManager.LegacyPackageDeleteObserver;
-import android.content.pm.PackageManager.PackageInfoFlags;
import android.content.pm.PackageManagerInternal.PackageListObserver;
import android.content.pm.PackageParser;
import android.content.pm.PackageParser.ActivityIntentInfo;
@@ -18267,7 +18267,8 @@
null /*enabledComponents*/,
null /*disabledComponents*/,
ps.readUserState(nextUserId).domainVerificationStatus,
- 0, PackageManager.INSTALL_REASON_UNKNOWN);
+ 0, PackageManager.INSTALL_REASON_UNKNOWN,
+ null /*harmfulAppWarning*/);
}
mSettings.writeKernelMappingLPr(ps);
}
@@ -23579,6 +23580,47 @@
}
return unusedPackages;
}
+
+ @Override
+ public void setHarmfulAppWarning(@NonNull String packageName, @Nullable CharSequence warning,
+ int userId) {
+ final int callingUid = Binder.getCallingUid();
+ final int callingAppId = UserHandle.getAppId(callingUid);
+
+ mPermissionManager.enforceCrossUserPermission(callingUid, userId,
+ true /*requireFullPermission*/, true /*checkShell*/, "setHarmfulAppInfo");
+
+ if (callingAppId != Process.SYSTEM_UID && callingAppId != Process.ROOT_UID &&
+ checkUidPermission(SET_HARMFUL_APP_WARNINGS, callingUid) != PERMISSION_GRANTED) {
+ throw new SecurityException("Caller must have the "
+ + SET_HARMFUL_APP_WARNINGS + " permission.");
+ }
+
+ synchronized(mPackages) {
+ mSettings.setHarmfulAppWarningLPw(packageName, warning, userId);
+ scheduleWritePackageRestrictionsLocked(userId);
+ }
+ }
+
+ @Nullable
+ @Override
+ public CharSequence getHarmfulAppWarning(@NonNull String packageName, int userId) {
+ final int callingUid = Binder.getCallingUid();
+ final int callingAppId = UserHandle.getAppId(callingUid);
+
+ mPermissionManager.enforceCrossUserPermission(callingUid, userId,
+ true /*requireFullPermission*/, true /*checkShell*/, "getHarmfulAppInfo");
+
+ if (callingAppId != Process.SYSTEM_UID && callingAppId != Process.ROOT_UID &&
+ checkUidPermission(SET_HARMFUL_APP_WARNINGS, callingUid) != PERMISSION_GRANTED) {
+ throw new SecurityException("Caller must have the "
+ + SET_HARMFUL_APP_WARNINGS + " permission.");
+ }
+
+ synchronized(mPackages) {
+ return mSettings.getHarmfulAppWarningLPr(packageName, userId);
+ }
+ }
}
interface PackageSender {
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 2d82c46..bd1cd33 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -230,6 +230,8 @@
return runGetInstantAppResolver();
case "has-feature":
return runHasFeature();
+ case "set-harmful-app-warning":
+ return runSetHarmfulAppWarning();
default: {
String nextArg = getNextArg();
if (nextArg == null) {
@@ -1277,7 +1279,7 @@
return runRemoveSplit(packageName, splitName);
}
- userId = translateUserId(userId, "runUninstall");
+ userId = translateUserId(userId, true /*allowAll*/, "runUninstall");
if (userId == UserHandle.USER_ALL) {
userId = UserHandle.USER_SYSTEM;
flags |= PackageManager.DELETE_ALL_USERS;
@@ -2088,6 +2090,29 @@
return 0;
}
+ private int runSetHarmfulAppWarning() throws RemoteException {
+ int userId = UserHandle.USER_CURRENT;
+
+ String opt;
+ while ((opt = getNextOption()) != null) {
+ if (opt.equals("--user")) {
+ userId = UserHandle.parseUserArg(getNextArgRequired());
+ } else {
+ getErrPrintWriter().println("Error: Unknown option: " + opt);
+ return -1;
+ }
+ }
+
+ userId = translateUserId(userId, false /*allowAll*/, "runSetHarmfulAppWarning");
+
+ final String packageName = getNextArgRequired();
+ final String warning = getNextArg();
+
+ mInterface.setHarmfulAppWarning(packageName, warning, userId);
+
+ return 0;
+ }
+
private static String checkAbiArgument(String abi) {
if (TextUtils.isEmpty(abi)) {
throw new IllegalArgumentException("Missing ABI argument");
@@ -2107,14 +2132,14 @@
throw new IllegalArgumentException("ABI " + abi + " not supported on this device");
}
- private int translateUserId(int userId, String logContext) {
+ private int translateUserId(int userId, boolean allowAll, String logContext) {
return ActivityManager.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
- userId, true, true, logContext, "pm command");
+ userId, allowAll, true, logContext, "pm command");
}
private int doCreateSession(SessionParams params, String installerPackageName, int userId)
throws RemoteException {
- userId = translateUserId(userId, "runInstallCreate");
+ userId = translateUserId(userId, true /*allowAll*/, "runInstallCreate");
if (userId == UserHandle.USER_ALL) {
userId = UserHandle.USER_SYSTEM;
params.installFlags |= PackageManager.INSTALL_ALL_USERS;
@@ -2634,6 +2659,9 @@
pw.println("");
pw.println(" get-instantapp-resolver");
pw.println(" Return the name of the component that is the current instant app installer.");
+ pw.println("");
+ pw.println(" set-harmful-app-warning [--user <USER_ID>] <PACKAGE> [<WARNING>]");
+ pw.println(" Mark the app as harmful with the given warning message.");
pw.println();
Intent.printIntentArgsHelp(pw , "");
}
diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java
index 809e16c..e3c4c43 100644
--- a/services/core/java/com/android/server/pm/PackageSettingBase.java
+++ b/services/core/java/com/android/server/pm/PackageSettingBase.java
@@ -437,7 +437,8 @@
boolean notLaunched, boolean hidden, boolean suspended, boolean instantApp,
boolean virtualPreload, String lastDisableAppCaller,
ArraySet<String> enabledComponents, ArraySet<String> disabledComponents,
- int domainVerifState, int linkGeneration, int installReason) {
+ int domainVerifState, int linkGeneration, int installReason,
+ String harmfulAppWarning) {
PackageUserState state = modifyUserState(userId);
state.ceDataInode = ceDataInode;
state.enabled = enabled;
@@ -454,6 +455,7 @@
state.installReason = installReason;
state.instantApp = instantApp;
state.virtualPreload = virtualPreload;
+ state.harmfulAppWarning = harmfulAppWarning;
}
ArraySet<String> getEnabledComponents(int userId) {
@@ -620,4 +622,14 @@
proto.end(userToken);
}
}
+
+ void setHarmfulAppWarning(int userId, String harmfulAppWarning) {
+ PackageUserState userState = modifyUserState(userId);
+ userState.harmfulAppWarning = harmfulAppWarning;
+ }
+
+ String getHarmfulAppWarning(int userId) {
+ PackageUserState userState = readUserState(userId);
+ return userState.harmfulAppWarning;
+ }
}
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index 4cf1814..c3884d2 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -227,6 +227,7 @@
private static final String ATTR_INSTALL_REASON = "install-reason";
private static final String ATTR_INSTANT_APP = "instant-app";
private static final String ATTR_VIRTUAL_PRELOAD = "virtual-preload";
+ private static final String ATTR_HARMFUL_APP_WARNING = "harmful-app-warning";
private static final String ATTR_PACKAGE_NAME = "packageName";
private static final String ATTR_FINGERPRINT = "fingerprint";
@@ -742,7 +743,8 @@
null /*enabledComponents*/,
null /*disabledComponents*/,
INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED,
- 0, PackageManager.INSTALL_REASON_UNKNOWN);
+ 0, PackageManager.INSTALL_REASON_UNKNOWN,
+ null /*harmfulAppWarning*/);
}
}
}
@@ -1680,7 +1682,8 @@
null /*enabledComponents*/,
null /*disabledComponents*/,
INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED,
- 0, PackageManager.INSTALL_REASON_UNKNOWN);
+ 0, PackageManager.INSTALL_REASON_UNKNOWN,
+ null /*harmfulAppWarning*/);
}
return;
}
@@ -1755,7 +1758,8 @@
COMPONENT_ENABLED_STATE_DEFAULT);
final String enabledCaller = parser.getAttributeValue(null,
ATTR_ENABLED_CALLER);
-
+ final String harmfulAppWarning =
+ parser.getAttributeValue(null, ATTR_HARMFUL_APP_WARNING);
final int verifState = XmlUtils.readIntAttribute(parser,
ATTR_DOMAIN_VERIFICATON_STATE,
PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED);
@@ -1792,7 +1796,7 @@
ps.setUserState(userId, ceDataInode, enabled, installed, stopped, notLaunched,
hidden, suspended, instantApp, virtualPreload, enabledCaller,
enabledComponents, disabledComponents, verifState, linkGeneration,
- installReason);
+ installReason, harmfulAppWarning);
} else if (tagName.equals("preferred-activities")) {
readPreferredActivitiesLPw(parser, userId);
} else if (tagName.equals(TAG_PERSISTENT_PREFERRED_ACTIVITIES)) {
@@ -2125,6 +2129,10 @@
serializer.attribute(null, ATTR_INSTALL_REASON,
Integer.toString(ustate.installReason));
}
+ if (ustate.harmfulAppWarning != null) {
+ serializer.attribute(null, ATTR_HARMFUL_APP_WARNING,
+ ustate.harmfulAppWarning);
+ }
if (!ArrayUtils.isEmpty(ustate.enabledComponents)) {
serializer.startTag(null, TAG_ENABLED_COMPONENTS);
for (final String name : ustate.enabledComponents) {
@@ -4347,6 +4355,22 @@
return false;
}
+ void setHarmfulAppWarningLPw(String packageName, CharSequence warning, int userId) {
+ final PackageSetting pkgSetting = mPackages.get(packageName);
+ if (pkgSetting == null) {
+ throw new IllegalArgumentException("Unknown package: " + packageName);
+ }
+ pkgSetting.setHarmfulAppWarning(userId, warning == null ? null : warning.toString());
+ }
+
+ String getHarmfulAppWarningLPr(String packageName, int userId) {
+ final PackageSetting pkgSetting = mPackages.get(packageName);
+ if (pkgSetting == null) {
+ throw new IllegalArgumentException("Unknown package: " + packageName);
+ }
+ return pkgSetting.getHarmfulAppWarning(userId);
+ }
+
private static List<UserInfo> getAllUsers(UserManagerService userManager) {
long id = Binder.clearCallingIdentity();
try {
@@ -4493,11 +4517,14 @@
pw.print(ps.getNotLaunched(user.id) ? "l" : "L");
pw.print(ps.getInstantApp(user.id) ? "IA" : "ia");
pw.print(ps.getVirtulalPreload(user.id) ? "VPI" : "vpi");
+ String harmfulAppWarning = ps.getHarmfulAppWarning(user.id);
+ pw.print(harmfulAppWarning != null ? "HA" : "ha");
pw.print(",");
pw.print(ps.getEnabled(user.id));
String lastDisabledAppCaller = ps.getLastDisabledAppCaller(user.id);
pw.print(",");
pw.print(lastDisabledAppCaller != null ? lastDisabledAppCaller : "?");
+ pw.print(",");
pw.println();
}
return;
@@ -4772,6 +4799,12 @@
.getRuntimePermissionStates(user.id), dumpAll);
}
+ String harmfulAppWarning = ps.getHarmfulAppWarning(user.id);
+ if (harmfulAppWarning != null) {
+ pw.print(prefix); pw.print(" harmfulAppWarning: ");
+ pw.println(harmfulAppWarning);
+ }
+
if (permissionNames == null) {
ArraySet<String> cmp = ps.getDisabledComponents(user.id);
if (cmp != null && cmp.size() > 0) {
diff --git a/test-mock/src/android/test/mock/MockPackageManager.java b/test-mock/src/android/test/mock/MockPackageManager.java
index 7207ebc..13e3693 100644
--- a/test-mock/src/android/test/mock/MockPackageManager.java
+++ b/test-mock/src/android/test/mock/MockPackageManager.java
@@ -1174,4 +1174,20 @@
public ArtManager getArtManager() {
throw new UnsupportedOperationException();
}
+
+ /**
+ * @hide
+ */
+ @Override
+ public void setHarmfulAppWarning(String packageName, CharSequence warning) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * @hide
+ */
+ @Override
+ public CharSequence getHarmfulAppWarning(String packageName) {
+ throw new UnsupportedOperationException();
+ }
}