summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--core/java/android/app/ActivityManagerNative.java20
-rw-r--r--core/java/android/app/ApplicationPackageManager.java20
-rw-r--r--core/java/android/app/ContextImpl.java10
-rw-r--r--core/java/android/app/IActivityManager.java5
-rw-r--r--core/java/android/content/Context.java10
-rw-r--r--core/java/android/content/ContextWrapper.java5
-rw-r--r--core/java/android/content/pm/IPackageManager.aidl6
-rw-r--r--core/java/android/content/pm/PackageManager.java16
-rw-r--r--core/java/android/content/pm/PackageUserState.java3
-rw-r--r--core/java/android/database/MemoryCursor.java50
-rw-r--r--core/java/android/provider/Settings.java18
-rw-r--r--core/res/AndroidManifest.xml6
-rw-r--r--core/res/res/drawable-hdpi/stat_notify_privacy_guard.pngbin0 -> 1302 bytes
-rw-r--r--core/res/res/drawable-mdpi/stat_notify_privacy_guard.pngbin0 -> 1188 bytes
-rw-r--r--core/res/res/drawable-xhdpi/stat_notify_privacy_guard.pngbin0 -> 1421 bytes
-rw-r--r--core/res/res/drawable-xxhdpi/stat_notify_privacy_guard.pngbin0 -> 1688 bytes
-rw-r--r--core/res/res/values/cm_strings.xml5
-rw-r--r--core/res/res/values/symbols.xml4
-rw-r--r--services/java/com/android/server/LocationManagerService.java58
-rw-r--r--services/java/com/android/server/am/ActivityManagerService.java91
-rw-r--r--services/java/com/android/server/am/ActivityStack.java34
-rw-r--r--services/java/com/android/server/am/BroadcastQueue.java15
-rw-r--r--services/java/com/android/server/pm/PackageManagerService.java59
-rw-r--r--services/java/com/android/server/pm/PackageSettingBase.java11
-rw-r--r--services/java/com/android/server/pm/Settings.java25
-rw-r--r--test-runner/src/android/test/mock/MockContext.java5
-rw-r--r--test-runner/src/android/test/mock/MockPackageManager.java10
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java6
28 files changed, 484 insertions, 8 deletions
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 61b2067..b4cc515 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -1818,6 +1818,14 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM
return true;
}
+ case IS_PRIVACY_GUARD_ENABLED_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ int pid = data.readInt();
+ boolean res = isPrivacyGuardEnabledForProcess(pid);
+ reply.writeNoException();
+ reply.writeInt(res ? 1 : 0);
+ return true;
+ }
}
return super.onTransact(code, data, reply, flags);
@@ -4149,5 +4157,17 @@ class ActivityManagerProxy implements IActivityManager
return res;
}
+ public boolean isPrivacyGuardEnabledForProcess(int pid) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeInt(pid);
+ mRemote.transact(IS_PRIVACY_GUARD_ENABLED_TRANSACTION, data, reply, 0);
+ reply.readException();
+ int res = reply.readInt();
+ data.recycle();
+ reply.recycle();
+ return res == 1;
+ }
private IBinder mRemote;
}
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 0d11e29..aa7d98c 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -48,6 +48,7 @@ import android.content.res.Resources;
import android.content.res.XmlResourceParser;
import android.graphics.drawable.Drawable;
import android.net.Uri;
+import android.os.Binder;
import android.os.Process;
import android.os.RemoteException;
import android.util.Log;
@@ -1296,6 +1297,25 @@ final class ApplicationPackageManager extends PackageManager {
return PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
}
+ @Override
+ public void setPrivacyGuardSetting(String packageName, boolean enabled) {
+ try {
+ mPM.setPrivacyGuardSetting(packageName, enabled, mContext.getUserId());
+ } catch (RemoteException e) {
+ // Should never happen!
+ }
+ }
+
+ @Override
+ public boolean getPrivacyGuardSetting(String packageName) {
+ try {
+ return mPM.getPrivacyGuardSetting(packageName, mContext.getUserId());
+ } catch (RemoteException e) {
+ // Should never happen!
+ }
+ return false;
+ }
+
/**
* @hide
*/
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 4d9f11e..21fbc1f 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -1550,6 +1550,16 @@ class ContextImpl extends Context {
}
@Override
+ public boolean isPrivacyGuardEnabled() {
+ try {
+ return ActivityManagerNative.getDefault().isPrivacyGuardEnabledForProcess(Binder.getCallingPid());
+ } catch (RemoteException e) {
+ Log.e(TAG, e.getMessage(), e);
+ }
+ return false;
+ }
+
+ @Override
public int checkPermission(String permission, int pid, int uid) {
if (permission == null) {
throw new IllegalArgumentException("permission is null");
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 8af17a4..4d04e68 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -190,7 +190,9 @@ public interface IActivityManager extends IInterface {
public void setProcessForeground(IBinder token, int pid,
boolean isForeground) throws RemoteException;
-
+
+ public boolean isPrivacyGuardEnabledForProcess(int pid) throws RemoteException;
+
public int checkPermission(String permission, int pid, int uid)
throws RemoteException;
@@ -624,4 +626,5 @@ public interface IActivityManager extends IInterface {
int INPUT_DISPATCHING_TIMED_OUT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+158;
int CLEAR_PENDING_BACKUP_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+159;
int GET_INTENT_FOR_INTENT_SENDER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+160;
+ int IS_PRIVACY_GUARD_ENABLED_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+161;
}
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index f0c6ce8..bd7868f 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -2267,6 +2267,16 @@ public abstract class Context {
public static final String USER_SERVICE = "user";
/**
+ * Determine whether the application or calling application has
+ * privacy guard. This is a privacy feature intended to permit the user
+ * to control access to personal data. Applications and content providers
+ * can check this value if they wish to honor privacy guard.
+ *
+ * @hide
+ */
+ public abstract boolean isPrivacyGuardEnabled();
+
+ /**
* Determine whether the given permission is allowed for a particular
* process and user ID running in the system.
*
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index 84ad667..3f61075 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -496,6 +496,11 @@ public class ContextWrapper extends Context {
}
@Override
+ public boolean isPrivacyGuardEnabled() {
+ return mBase.isPrivacyGuardEnabled();
+ }
+
+ @Override
public int checkPermission(String permission, int pid, int uid) {
return mBase.checkPermission(permission, pid, uid);
}
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index d6a78ea..a5accc6 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -212,7 +212,11 @@ interface IPackageManager {
int getPreferredActivities(out List<IntentFilter> outFilters,
out List<ComponentName> outActivities, String packageName);
-
+
+ boolean getPrivacyGuardSetting(in String packageName, int userId);
+
+ void setPrivacyGuardSetting(in String packageName, boolean enabled, int userId);
+
/**
* As per {@link android.content.pm.PackageManager#setComponentEnabledSetting}.
*/
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index eb6e640..28d60c8 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -2941,6 +2941,22 @@ public abstract class PackageManager {
public abstract int getApplicationEnabledSetting(String packageName);
/**
+ * @param packageName
+ * @return
+ *
+ * @hide
+ */
+ public abstract boolean getPrivacyGuardSetting(String packageName);
+
+ /**
+ * @param packageName
+ * @param enabled
+ *
+ * @hide
+ */
+ public abstract void setPrivacyGuardSetting(String packageName, boolean enabled);
+
+ /**
* Return whether the device has been booted into safe mode.
*/
public abstract boolean isSafeMode();
diff --git a/core/java/android/content/pm/PackageUserState.java b/core/java/android/content/pm/PackageUserState.java
index 3579977..91ef1bc 100644
--- a/core/java/android/content/pm/PackageUserState.java
+++ b/core/java/android/content/pm/PackageUserState.java
@@ -29,6 +29,7 @@ public class PackageUserState {
public boolean notLaunched;
public boolean installed;
public int enabled;
+ public boolean privacyGuard;
public HashSet<String> disabledComponents;
public HashSet<String> enabledComponents;
@@ -36,6 +37,7 @@ public class PackageUserState {
public PackageUserState() {
installed = true;
enabled = COMPONENT_ENABLED_STATE_DEFAULT;
+ privacyGuard = false;
}
public PackageUserState(PackageUserState o) {
@@ -43,6 +45,7 @@ public class PackageUserState {
stopped = o.stopped;
notLaunched = o.notLaunched;
enabled = o.enabled;
+ privacyGuard = o.privacyGuard;
disabledComponents = o.disabledComponents != null
? new HashSet<String>(o.disabledComponents) : null;
enabledComponents = o.enabledComponents != null
diff --git a/core/java/android/database/MemoryCursor.java b/core/java/android/database/MemoryCursor.java
new file mode 100644
index 0000000..7eb71db
--- /dev/null
+++ b/core/java/android/database/MemoryCursor.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2011 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.database;
+
+import android.database.AbstractWindowedCursor;
+import android.database.Cursor;
+import android.database.CursorWindow;
+import android.database.DatabaseUtils;
+
+/**
+ * Implementation of an in-memory cursor backed by a cursor window.
+ *
+ * @hide
+ */
+public class MemoryCursor extends AbstractWindowedCursor {
+
+ private final String[] mColumnNames;
+
+ public MemoryCursor(String name, String[] columnNames) {
+ setWindow(new CursorWindow(name));
+ mColumnNames = columnNames;
+ }
+
+ public void fillFromCursor(Cursor cursor) {
+ DatabaseUtils.cursorFillWindow(cursor, 0, getWindow());
+ }
+
+ @Override
+ public int getCount() {
+ return getWindow().getNumRows();
+ }
+
+ @Override
+ public String[] getColumnNames() {
+ return mColumnNames;
+ }
+}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 90b9b32..22fa2fa 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -18,6 +18,7 @@ package android.provider;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
+import android.app.ActivityManagerNative;
import android.app.SearchManager;
import android.app.WallpaperManager;
import android.content.ComponentName;
@@ -37,6 +38,7 @@ import android.net.ConnectivityManager;
import android.net.Uri;
import android.net.wifi.WifiManager;
import android.os.BatteryManager;
+import android.os.Binder;
import android.os.Bundle;
import android.os.DropBoxManager;
import android.os.IBinder;
@@ -5021,6 +5023,12 @@ public final class Settings {
public static final String ADVANCED_REBOOT = "advanced_reboot";
/**
+ * Whether newly installed apps should run with privacy guard by default
+ * @hide
+ */
+ public static final String PRIVACY_GUARD_DEFAULT = "privacy_guard_default";
+
+ /**
* This are the settings to be backed up.
*
* NOTE: Settings are backed up and restored in the order they appear
@@ -5063,7 +5071,8 @@ public final class Settings {
UI_NIGHT_MODE,
LOCK_SCREEN_OWNER_INFO,
LOCK_SCREEN_OWNER_INFO_ENABLED,
- ADVANCED_REBOOT
+ ADVANCED_REBOOT,
+ PRIVACY_GUARD_DEFAULT
};
/**
@@ -5085,6 +5094,13 @@ public final class Settings {
* @hide
*/
public static final boolean isLocationProviderEnabledForUser(ContentResolver cr, String provider, int userId) {
+ try {
+ if (ActivityManagerNative.getDefault().isPrivacyGuardEnabledForProcess(Binder.getCallingPid())) {
+ return false;
+ }
+ } catch (RemoteException e) {
+ // ignore
+ }
String allowedProviders = Settings.Secure.getStringForUser(cr,
LOCATION_PROVIDERS_ALLOWED, userId);
return TextUtils.delimitedStringContains(allowedProviders, ',', provider);
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 373d018..bac9ff6 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1856,6 +1856,12 @@
android:description="@string/permdesc_movePackage"
android:protectionLevel="signature|system" />
+ <!-- Allows an application to change privacy guard state -->
+ <permission android:name="android.permission.CHANGE_PRIVACY_GUARD_STATE"
+ android:label="@string/permlab_changePrivacyGuardState"
+ android:description="@string/permdesc_changePrivacyGuardState"
+ android:protectionLevel="signature" />
+
<!-- Allows an application to change whether an application component (other than its own) is
enabled or not. -->
<permission android:name="android.permission.CHANGE_COMPONENT_ENABLED_STATE"
diff --git a/core/res/res/drawable-hdpi/stat_notify_privacy_guard.png b/core/res/res/drawable-hdpi/stat_notify_privacy_guard.png
new file mode 100644
index 0000000..ac7f817
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_notify_privacy_guard.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_notify_privacy_guard.png b/core/res/res/drawable-mdpi/stat_notify_privacy_guard.png
new file mode 100644
index 0000000..0e72a79
--- /dev/null
+++ b/core/res/res/drawable-mdpi/stat_notify_privacy_guard.png
Binary files differ
diff --git a/core/res/res/drawable-xhdpi/stat_notify_privacy_guard.png b/core/res/res/drawable-xhdpi/stat_notify_privacy_guard.png
new file mode 100644
index 0000000..c5e70a2
--- /dev/null
+++ b/core/res/res/drawable-xhdpi/stat_notify_privacy_guard.png
Binary files differ
diff --git a/core/res/res/drawable-xxhdpi/stat_notify_privacy_guard.png b/core/res/res/drawable-xxhdpi/stat_notify_privacy_guard.png
new file mode 100644
index 0000000..4f34941
--- /dev/null
+++ b/core/res/res/drawable-xxhdpi/stat_notify_privacy_guard.png
Binary files differ
diff --git a/core/res/res/values/cm_strings.xml b/core/res/res/values/cm_strings.xml
index 8b89423..64ea2be 100644
--- a/core/res/res/values/cm_strings.xml
+++ b/core/res/res/values/cm_strings.xml
@@ -158,4 +158,9 @@
<string name="symbol_picker_lt">\u2264\u00ab\u2039</string>
<string name="symbol_picker_gt">\u2265\u00bb\u203a</string>
+ <!-- Privacy Guard -->
+ <string name="permlab_changePrivacyGuardState">enable or disable privacy guard</string>
+ <string name="permdesc_changePrivacyGuardState">Allows the application to change whether another application runs with Privacy Guard. When an application is running with Privacy Guard, it will not have access to personal data such as contacts, call logs, or messages.</string>
+ <string name="privacy_guard_notification">Privacy Guard active</string>
+ <string name="privacy_guard_notification_detail"><xliff:g id="app">%1$s</xliff:g> will not be able to access personal data</string>
</resources>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 0ff9036..3679acb 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2003,4 +2003,8 @@
<!-- Electron Beam -->
<java-symbol type="bool" name="config_screenOffAnimation" />
+ <!-- Privacy Guard -->
+ <java-symbol type="string" name="privacy_guard_notification" />
+ <java-symbol type="string" name="privacy_guard_notification_detail" />
+ <java-symbol type="drawable" name="stat_notify_privacy_guard" />
</resources>
diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java
index 0f08c56..4658156 100644
--- a/services/java/com/android/server/LocationManagerService.java
+++ b/services/java/com/android/server/LocationManagerService.java
@@ -16,6 +16,7 @@
package com.android.server;
+import android.app.ActivityManagerNative;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.ContentResolver;
@@ -664,8 +665,20 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
mProvidersByName.remove(provider.getName());
}
+ private boolean isPrivacyGuardEnabled(int pid) {
+ try {
+ if (ActivityManagerNative.getDefault().isPrivacyGuardEnabledForProcess(pid)) {
+ Slog.i(TAG, "Location services unavailable under privacy guard for process pid=" + pid);
+ return true;
+ }
+ } catch (RemoteException e) {
+ // nothing
+ }
+ return false;
+ }
private boolean isAllowedBySettingsLocked(String provider, int userId) {
+
if (userId != mCurrentUserId) {
return false;
}
@@ -826,6 +839,9 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
*/
@Override
public List<String> getProviders(Criteria criteria, boolean enabledOnly) {
+ if (isPrivacyGuardEnabled(Binder.getCallingPid())) {
+ return new ArrayList<String>(0);
+ }
int allowedResolutionLevel = getCallerAllowedResolutionLevel();
ArrayList<String> out;
int callingUserId = UserHandle.getCallingUserId();
@@ -1224,7 +1240,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
}
boolean isProviderEnabled = isAllowedBySettingsLocked(name, UserHandle.getUserId(uid));
- if (isProviderEnabled) {
+ if (isProviderEnabled && !isPrivacyGuardEnabled(pid)) {
applyRequirementsLocked(name);
} else {
// Notify the listener that updates are currently disabled
@@ -1238,6 +1254,9 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
checkPackageName(packageName);
final int pid = Binder.getCallingPid();
+ if (isPrivacyGuardEnabled(pid)) {
+ return;
+ }
final int uid = Binder.getCallingUid();
Receiver receiver = checkListenerOrIntent(listener, intent, pid, uid, packageName);
@@ -1297,6 +1316,9 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
checkResolutionLevelIsSufficientForProviderUse(allowedResolutionLevel,
request.getProvider());
// no need to sanitize this request, as only the provider name is used
+ if (isPrivacyGuardEnabled(Binder.getCallingPid())) {
+ return null;
+ }
long identity = Binder.clearCallingIdentity();
try {
@@ -1349,8 +1371,13 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
if (D) Log.d(TAG, "requestGeofence: " + sanitizedRequest + " " + geofence + " " + intent);
+ if (isPrivacyGuardEnabled(Binder.getCallingPid())) {
+ return;
+ }
+
// geo-fence manager uses the public location API, need to clear identity
int uid = Binder.getCallingUid();
+
if (UserHandle.getUserId(uid) != UserHandle.USER_OWNER) {
// temporary measure until geofences work for secondary users
Log.w(TAG, "proximity alerts are currently available only to the primary user");
@@ -1372,6 +1399,10 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
if (D) Log.d(TAG, "removeGeofence: " + geofence + " " + intent);
+ if (isPrivacyGuardEnabled(Binder.getCallingPid())) {
+ return;
+ }
+
// geo-fence manager uses the public location API, need to clear identity
long identity = Binder.clearCallingIdentity();
try {
@@ -1390,6 +1421,10 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
checkResolutionLevelIsSufficientForProviderUse(getCallerAllowedResolutionLevel(),
LocationManager.GPS_PROVIDER);
+ if (isPrivacyGuardEnabled(Binder.getCallingPid())) {
+ return false;
+ }
+
try {
mGpsStatusProvider.addGpsStatusListener(listener);
} catch (RemoteException e) {
@@ -1401,6 +1436,10 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
@Override
public void removeGpsStatusListener(IGpsStatusListener listener) {
+ if (isPrivacyGuardEnabled(Binder.getCallingPid())) {
+ return;
+ }
+
synchronized (mLock) {
try {
mGpsStatusProvider.removeGpsStatusListener(listener);
@@ -1419,6 +1458,10 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
checkResolutionLevelIsSufficientForProviderUse(getCallerAllowedResolutionLevel(),
provider);
+ if (isPrivacyGuardEnabled(Binder.getCallingPid())) {
+ return false;
+ }
+
// and check for ACCESS_LOCATION_EXTRA_COMMANDS
if ((mContext.checkCallingOrSelfPermission(ACCESS_LOCATION_EXTRA_COMMANDS)
!= PackageManager.PERMISSION_GRANTED)) {
@@ -1439,6 +1482,11 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
throw new SecurityException(
"calling sendNiResponse from outside of the system is not allowed");
}
+
+ if (isPrivacyGuardEnabled(Binder.getCallingPid())) {
+ return false;
+ }
+
try {
return mNetInitiatedListener.sendNiResponse(notifId, userResponse);
} catch (RemoteException e) {
@@ -1461,6 +1509,10 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
checkResolutionLevelIsSufficientForProviderUse(getCallerAllowedResolutionLevel(),
provider);
+ if (isPrivacyGuardEnabled(Binder.getCallingPid())) {
+ return null;
+ }
+
LocationProviderInterface p;
synchronized (mLock) {
p = mProvidersByName.get(provider);
@@ -1476,6 +1528,10 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
provider);
if (LocationManager.FUSED_PROVIDER.equals(provider)) return false;
+ if (isPrivacyGuardEnabled(Binder.getCallingPid())) {
+ return false;
+ }
+
long identity = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index c068982..618f02c 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -120,6 +120,7 @@ import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.provider.Settings;
+import android.provider.Telephony.Sms.Intents;
import android.text.format.Time;
import android.util.EventLog;
import android.util.Log;
@@ -901,6 +902,9 @@ public final class ActivityManagerService extends ActivityManagerNative
static final int CONTINUE_USER_SWITCH_MSG = 35;
static final int USER_SWITCH_TIMEOUT_MSG = 36;
+ static final int POST_PRIVACY_NOTIFICATION_MSG = 40;
+ static final int CANCEL_PRIVACY_NOTIFICATION_MSG = 41;
+
static final int FIRST_ACTIVITY_STACK_MSG = 100;
static final int FIRST_BROADCAST_QUEUE_MSG = 200;
static final int FIRST_COMPAT_MODE_MSG = 300;
@@ -1361,6 +1365,69 @@ public final class ActivityManagerService extends ActivityManagerNative
timeoutUserSwitch((UserStartedState)msg.obj, msg.arg1, msg.arg2);
break;
}
+ case POST_PRIVACY_NOTIFICATION_MSG: {
+ INotificationManager inm = NotificationManager.getService();
+ if (inm == null) {
+ return;
+ }
+
+ ActivityRecord root = (ActivityRecord)msg.obj;
+ ProcessRecord process = root.app;
+ if (process == null) {
+ return;
+ }
+
+ try {
+ Context context = mContext.createPackageContext(process.info.packageName, 0);
+ String text = mContext.getString(R.string.privacy_guard_notification_detail,
+ context.getApplicationInfo().loadLabel(context.getPackageManager()));
+ String title = mContext.getString(R.string.privacy_guard_notification);
+
+ Intent infoIntent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
+ Uri.fromParts("package", root.packageName, null));
+
+ Notification notification = new Notification();
+ notification.icon = com.android.internal.R.drawable.stat_notify_privacy_guard;
+ notification.when = 0;
+ notification.flags = Notification.FLAG_ONGOING_EVENT;
+ notification.priority = Notification.PRIORITY_LOW;
+ notification.defaults = 0;
+ notification.sound = null;
+ notification.vibrate = null;
+ notification.setLatestEventInfo(getUiContext(),
+ title, text,
+ PendingIntent.getActivityAsUser(mContext, 0, infoIntent,
+ PendingIntent.FLAG_CANCEL_CURRENT, null,
+ new UserHandle(root.userId)));
+
+ try {
+ int[] outId = new int[1];
+ inm.enqueueNotificationWithTag("android", null,
+ R.string.privacy_guard_notification,
+ notification, outId, root.userId);
+ } catch (RuntimeException e) {
+ Slog.w(ActivityManagerService.TAG,
+ "Error showing notification for privacy guard", e);
+ } catch (RemoteException e) {
+ }
+ } catch (NameNotFoundException e) {
+ Slog.w(TAG, "Unable to create context for privacy guard notification", e);
+ }
+ } break;
+ case CANCEL_PRIVACY_NOTIFICATION_MSG: {
+ INotificationManager inm = NotificationManager.getService();
+ if (inm == null) {
+ return;
+ }
+ try {
+ inm.cancelNotificationWithTag("android", null,
+ R.string.privacy_guard_notification, msg.arg1);
+ } catch (RuntimeException e) {
+ Slog.w(ActivityManagerService.TAG,
+ "Error canceling notification for service", e);
+ } catch (RemoteException e) {
+ }
+ } break;
}
}
};
@@ -7426,6 +7493,30 @@ public final class ActivityManagerService extends ActivityManagerNative
return KEY_DISPATCHING_TIMEOUT;
}
+ public boolean isPrivacyGuardEnabledForProcess(int pid) {
+ ProcessRecord proc;
+ synchronized (mPidsSelfLocked) {
+ proc = mPidsSelfLocked.get(pid);
+ }
+ if (proc == null) {
+ return false;
+ }
+ try {
+ return AppGlobals.getPackageManager().getPrivacyGuardSetting(
+ proc.info.packageName, proc.userId);
+ } catch (RemoteException e) {
+ Log.e(TAG, e.getMessage(), e);
+ }
+ return false;
+ }
+
+ public boolean isFilteredByPrivacyGuard(String intent) {
+ return Intents.SMS_RECEIVED_ACTION.equals(intent) ||
+ Intents.DATA_SMS_RECEIVED_ACTION.equals(intent) ||
+ Intents.SMS_EMERGENCY_CB_RECEIVED_ACTION.equals(intent) ||
+ Intents.SMS_CB_RECEIVED_ACTION.equals(intent);
+ }
+
public void registerProcessObserver(IProcessObserver observer) {
enforceCallingPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER,
"registerProcessObserver()");
diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java
index 885ec89..4e9973f 100644
--- a/services/java/com/android/server/am/ActivityStack.java
+++ b/services/java/com/android/server/am/ActivityStack.java
@@ -1803,9 +1803,43 @@ final class ActivityStack {
startSpecificActivityLocked(next, true, true);
}
+ handlePrivacyGuardNotification(prev, next);
+
return true;
}
+ private final void handlePrivacyGuardNotification(ActivityRecord prev, ActivityRecord next) {
+ boolean curPrivacy = false;
+ boolean prevPrivacy = false;
+
+ if (next != null) {
+ try {
+ curPrivacy = AppGlobals.getPackageManager().getPrivacyGuardSetting(
+ next.packageName, next.userId);
+ } catch (RemoteException e) {
+ // nothing
+ }
+ }
+ if (prev != null) {
+ try {
+ prevPrivacy = AppGlobals.getPackageManager().getPrivacyGuardSetting(
+ prev.packageName, prev.userId);
+ } catch (RemoteException e) {
+ // nothing
+ }
+ }
+ if (prevPrivacy && !curPrivacy) {
+ Message msg = mService.mHandler.obtainMessage(
+ ActivityManagerService.CANCEL_PRIVACY_NOTIFICATION_MSG, prev.userId);
+ msg.sendToTarget();
+ } else if ((!prevPrivacy && curPrivacy) ||
+ (prevPrivacy && curPrivacy && !next.packageName.equals(prev.packageName))) {
+ Message msg = mService.mHandler.obtainMessage(
+ ActivityManagerService.POST_PRIVACY_NOTIFICATION_MSG, next);
+ msg.sendToTarget();
+ }
+ }
+
private final void startActivityLocked(ActivityRecord r, boolean newTask,
boolean doResume, boolean keepCurTransition, Bundle options) {
final int NH = mHistory.size();
diff --git a/services/java/com/android/server/am/BroadcastQueue.java b/services/java/com/android/server/am/BroadcastQueue.java
index bada7f0..fc4ecda 100644
--- a/services/java/com/android/server/am/BroadcastQueue.java
+++ b/services/java/com/android/server/am/BroadcastQueue.java
@@ -21,6 +21,7 @@ import java.io.PrintWriter;
import java.util.ArrayList;
import android.app.ActivityManager;
+import android.app.ActivityManagerNative;
import android.app.AppGlobals;
import android.content.ComponentName;
import android.content.IIntentReceiver;
@@ -409,7 +410,19 @@ public class BroadcastQueue {
skip = true;
}
}
-
+ try {
+ if (!skip && mService.isFilteredByPrivacyGuard(r.intent.getAction()) &&
+ ActivityManagerNative.getDefault().isPrivacyGuardEnabledForProcess(filter.receiverList.pid)) {
+ Slog.w(TAG, "Skipping broadcast of "
+ + r.intent.toString()
+ + " to " + filter.receiverList.app
+ + " (pid=" + filter.receiverList.pid
+ + ") due to privacy guard");
+ skip = true;
+ }
+ } catch (RemoteException e) {
+ // nothing
+ }
if (!skip) {
// If this is not being sent as an ordered broadcast, then we
// don't want to touch the fields that keep track of the current
diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java
index 461c060..7702deb 100644
--- a/services/java/com/android/server/pm/PackageManagerService.java
+++ b/services/java/com/android/server/pm/PackageManagerService.java
@@ -8485,11 +8485,16 @@ public class PackageManagerService extends IPackageManager.Stub {
// The caller is asking that the package only be deleted for a single
// user. To do this, we just mark its uninstalled state and delete
// its data.
+ boolean privacyGuard = android.provider.Settings.Secure.getIntForUser(
+ mContext.getContentResolver(),
+ android.provider.Settings.Secure.PRIVACY_GUARD_DEFAULT,
+ 0, user.getIdentifier()) == 1;
ps.setUserState(user.getIdentifier(),
COMPONENT_ENABLED_STATE_DEFAULT,
false, //installed
true, //stopped
true, //notLaunched
+ privacyGuard,
null, null);
if (ps.isAnyInstalled(sUserManager.getUserIds())) {
// Other user still have this package installed, so all
@@ -9033,6 +9038,60 @@ public class PackageManagerService extends IPackageManager.Stub {
}
@Override
+ public void setPrivacyGuardSetting(String appPackageName,
+ boolean enabled, int userId) {
+ if (!sUserManager.exists(userId)) return;
+ setPrivacyGuard(appPackageName, enabled, userId);
+ }
+
+ @Override
+ public boolean getPrivacyGuardSetting(String packageName, int userId) {
+ if (!sUserManager.exists(userId)) return false;
+ int uid = Binder.getCallingUid();
+ enforceCrossUserPermission(uid, userId, false, "get privacy guard");
+ // reader
+ synchronized (mPackages) {
+ return mSettings.getPrivacyGuardSettingLPr(packageName, userId);
+ }
+ }
+
+ private void setPrivacyGuard(final String packageName,
+ final boolean enabled, final int userId) {
+ PackageSetting pkgSetting;
+ final int uid = Binder.getCallingUid();
+ final int permission = mContext.checkCallingPermission(
+ android.Manifest.permission.CHANGE_PRIVACY_GUARD_STATE);
+ final boolean allowedByPermission = (permission == PackageManager.PERMISSION_GRANTED);
+ enforceCrossUserPermission(uid, userId, false, "set privacy guard");
+
+ synchronized (mPackages) {
+ pkgSetting = mSettings.mPackages.get(packageName);
+ if (pkgSetting == null) {
+ throw new IllegalArgumentException(
+ "Unknown package: " + packageName);
+ }
+ // Allow root and verify that userId is not being specified by a different user
+ if (!allowedByPermission && !UserHandle.isSameApp(uid, pkgSetting.appId)) {
+ throw new SecurityException(
+ "Permission Denial: attempt to change privacy guard state from pid="
+ + Binder.getCallingPid()
+ + ", uid=" + uid + ", package uid=" + pkgSetting.appId);
+ }
+ if (pkgSetting.isPrivacyGuard(userId) == enabled) {
+ // Nothing to do
+ return;
+ }
+ pkgSetting.setPrivacyGuard(enabled, userId);
+ mSettings.writePackageRestrictionsLPr(userId);
+ try {
+ ActivityManagerNative.getDefault().forceStopPackage(packageName, userId);
+ } catch (RemoteException e) {
+ //nothing
+ }
+ }
+ }
+
+ @Override
public void setApplicationEnabledSetting(String appPackageName,
int newState, int flags, int userId) {
if (!sUserManager.exists(userId)) return;
diff --git a/services/java/com/android/server/pm/PackageSettingBase.java b/services/java/com/android/server/pm/PackageSettingBase.java
index ae1b213..af45219 100644
--- a/services/java/com/android/server/pm/PackageSettingBase.java
+++ b/services/java/com/android/server/pm/PackageSettingBase.java
@@ -197,6 +197,14 @@ class PackageSettingBase extends GrantedPermissions {
return readUserState(userId).enabled;
}
+ void setPrivacyGuard(boolean enabled, int userId) {
+ modifyUserState(userId).privacyGuard = enabled;
+ }
+
+ boolean isPrivacyGuard(int userId) {
+ return readUserState(userId).privacyGuard;
+ }
+
void setInstalled(boolean inst, int userId) {
modifyUserState(userId).installed = inst;
}
@@ -249,13 +257,14 @@ class PackageSettingBase extends GrantedPermissions {
}
void setUserState(int userId, int enabled, boolean installed, boolean stopped,
- boolean notLaunched, HashSet<String> enabledComponents,
+ boolean notLaunched, boolean privacyGuard, HashSet<String> enabledComponents,
HashSet<String> disabledComponents) {
PackageUserState state = modifyUserState(userId);
state.enabled = enabled;
state.installed = installed;
state.stopped = stopped;
state.notLaunched = notLaunched;
+ state.privacyGuard = privacyGuard;
state.enabledComponents = enabledComponents;
state.disabledComponents = disabledComponents;
}
diff --git a/services/java/com/android/server/pm/Settings.java b/services/java/com/android/server/pm/Settings.java
index 1f568fb..f2074b0 100644
--- a/services/java/com/android/server/pm/Settings.java
+++ b/services/java/com/android/server/pm/Settings.java
@@ -100,6 +100,7 @@ final class Settings {
private static final String ATTR_ENABLED = "enabled";
private static final String ATTR_STOPPED = "stopped";
private static final String ATTR_INSTALLED = "inst";
+ private static final String ATTR_PRIVACY_GUARD = "privacy-guard";
private final File mSettingsFilename;
private final File mBackupSettingsFilename;
@@ -443,10 +444,15 @@ final class Settings {
final boolean installed = installUser == null
|| installUser.getIdentifier() == UserHandle.USER_ALL
|| installUser.getIdentifier() == user.id;
+ final boolean privacyGuard = android.provider.Settings.Secure.getIntForUser(
+ mContext.getContentResolver(),
+ android.provider.Settings.Secure.PRIVACY_GUARD_DEFAULT,
+ 0, user.id) == 1;
p.setUserState(user.id, COMPONENT_ENABLED_STATE_DEFAULT,
installed,
true, // stopped,
true, // notLaunched
+ privacyGuard,
null, null);
writePackageRestrictionsLPr(user.id);
}
@@ -844,6 +850,7 @@ final class Settings {
true, // installed
false, // stopped
false, // notLaunched
+ false, // privacy guard
null, null);
}
return;
@@ -898,6 +905,9 @@ final class Settings {
final String notLaunchedStr = parser.getAttributeValue(null, ATTR_NOT_LAUNCHED);
final boolean notLaunched = stoppedStr == null
? false : Boolean.parseBoolean(notLaunchedStr);
+ final String privacyGuardStr = parser.getAttributeValue(null, ATTR_PRIVACY_GUARD);
+ final boolean privacyGuard = privacyGuardStr == null
+ ? false : Boolean.parseBoolean(privacyGuardStr);
HashSet<String> enabledComponents = null;
HashSet<String> disabledComponents = null;
@@ -918,7 +928,7 @@ final class Settings {
}
}
- ps.setUserState(userId, enabled, installed, stopped, notLaunched,
+ ps.setUserState(userId, enabled, installed, stopped, notLaunched, privacyGuard,
enabledComponents, disabledComponents);
} else if (tagName.equals("preferred-activities")) {
readPreferredActivitiesLPw(parser, userId);
@@ -1024,7 +1034,7 @@ final class Settings {
for (final PackageSetting pkg : mPackages.values()) {
PackageUserState ustate = pkg.readUserState(userId);
- if (ustate.stopped || ustate.notLaunched || !ustate.installed
+ if (ustate.stopped || ustate.notLaunched || !ustate.installed || ustate.privacyGuard
|| ustate.enabled != COMPONENT_ENABLED_STATE_DEFAULT
|| (ustate.enabledComponents != null
&& ustate.enabledComponents.size() > 0)
@@ -1047,6 +1057,9 @@ final class Settings {
serializer.attribute(null, ATTR_ENABLED,
Integer.toString(ustate.enabled));
}
+ if (ustate.privacyGuard) {
+ serializer.attribute(null, ATTR_PRIVACY_GUARD, "true");
+ }
if (ustate.enabledComponents != null
&& ustate.enabledComponents.size() > 0) {
serializer.startTag(null, TAG_ENABLED_COMPONENTS);
@@ -2437,6 +2450,14 @@ final class Settings {
return pkg.installerPackageName;
}
+ boolean getPrivacyGuardSettingLPr(String packageName, int userId) {
+ final PackageSetting pkg = mPackages.get(packageName);
+ if (pkg == null) {
+ throw new IllegalArgumentException("Unknown package: " + packageName);
+ }
+ return pkg.isPrivacyGuard(userId);
+ }
+
int getApplicationEnabledSettingLPr(String packageName, int userId) {
final PackageSetting pkg = mPackages.get(packageName);
if (pkg == null) {
diff --git a/test-runner/src/android/test/mock/MockContext.java b/test-runner/src/android/test/mock/MockContext.java
index 1f815e7..1f54e46 100644
--- a/test-runner/src/android/test/mock/MockContext.java
+++ b/test-runner/src/android/test/mock/MockContext.java
@@ -432,6 +432,11 @@ public class MockContext extends Context {
}
@Override
+ public boolean isPrivacyGuardEnabled() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
public int checkPermission(String permission, int pid, int uid) {
throw new UnsupportedOperationException();
}
diff --git a/test-runner/src/android/test/mock/MockPackageManager.java b/test-runner/src/android/test/mock/MockPackageManager.java
index 35148e6..3d5c3e2 100644
--- a/test-runner/src/android/test/mock/MockPackageManager.java
+++ b/test-runner/src/android/test/mock/MockPackageManager.java
@@ -493,6 +493,16 @@ public class MockPackageManager extends PackageManager {
}
@Override
+ public void setPrivacyGuardSetting(String packageName, boolean enabled) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean getPrivacyGuardSetting(String packageName) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
public void addPreferredActivity(IntentFilter filter,
int match, ComponentName[] set, ComponentName activity) {
throw new UnsupportedOperationException();
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
index e2fced6..ce8ed4f 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
@@ -888,6 +888,12 @@ public final class BridgeContext extends Context {
}
@Override
+ public boolean isPrivacyGuardEnabled() {
+ // pass
+ return false;
+ }
+
+ @Override
public int checkPermission(String arg0, int arg1, int arg2) {
// pass
return 0;