diff options
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 Binary files differnew file mode 100644 index 0000000..ac7f817 --- /dev/null +++ b/core/res/res/drawable-hdpi/stat_notify_privacy_guard.png diff --git a/core/res/res/drawable-mdpi/stat_notify_privacy_guard.png b/core/res/res/drawable-mdpi/stat_notify_privacy_guard.png Binary files differnew file mode 100644 index 0000000..0e72a79 --- /dev/null +++ b/core/res/res/drawable-mdpi/stat_notify_privacy_guard.png diff --git a/core/res/res/drawable-xhdpi/stat_notify_privacy_guard.png b/core/res/res/drawable-xhdpi/stat_notify_privacy_guard.png Binary files differnew file mode 100644 index 0000000..c5e70a2 --- /dev/null +++ b/core/res/res/drawable-xhdpi/stat_notify_privacy_guard.png diff --git a/core/res/res/drawable-xxhdpi/stat_notify_privacy_guard.png b/core/res/res/drawable-xxhdpi/stat_notify_privacy_guard.png Binary files differnew file mode 100644 index 0000000..4f34941 --- /dev/null +++ b/core/res/res/drawable-xxhdpi/stat_notify_privacy_guard.png 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; |
