Merge "Automatically grant URI permission as needed." into nyc-mr1-dev
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 33ae553..6dd14fd 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -19,7 +19,9 @@
import android.annotation.NonNull;
import android.content.ComponentName;
import android.content.IIntentSender;
+import android.content.Intent;
import android.content.res.Configuration;
+import android.os.Bundle;
import android.os.IBinder;
import android.service.voice.IVoiceInteractionSession;
@@ -161,4 +163,11 @@
*/
public abstract void updatePersistentConfigurationForUser(@NonNull Configuration values,
int userId);
+
+ /**
+ * Create an {@link IIntentSender} to start an activity, as if {@code packageName} on
+ * user {@code userId} created it.
+ */
+ public abstract IIntentSender getActivityIntentSenderAsPackage(String packageName,
+ int userId, int requestCode, Intent intent, int flags, Bundle bOptions);
}
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 4104d72..fa943f2 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -3261,6 +3261,7 @@
contentView.setTextViewText(R.id.app_name_text, null);
contentView.setViewVisibility(R.id.chronometer, View.GONE);
contentView.setViewVisibility(R.id.header_text, View.GONE);
+ contentView.setTextViewText(R.id.header_text, null);
contentView.setViewVisibility(R.id.header_text_divider, View.GONE);
contentView.setViewVisibility(R.id.time_divider, View.GONE);
contentView.setViewVisibility(R.id.time, View.GONE);
diff --git a/core/java/android/content/pm/ILauncherApps.aidl b/core/java/android/content/pm/ILauncherApps.aidl
index 430c7e7..c19e638 100644
--- a/core/java/android/content/pm/ILauncherApps.aidl
+++ b/core/java/android/content/pm/ILauncherApps.aidl
@@ -51,7 +51,7 @@
in List shortcutIds, in ComponentName componentName, int flags, in UserHandle user);
void pinShortcuts(String callingPackage, String packageName, in List<String> shortcutIds,
in UserHandle user);
- boolean startShortcut(String callingPackage, String packageName, String id,
+ void startShortcut(String callingPackage, String packageName, String id,
in Rect sourceBounds, in Bundle startActivityOptions, int userId);
int getShortcutIconResId(String callingPackage, String packageName, String id,
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 5a5cedf..dae243b 100755
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -6259,6 +6259,24 @@
public static final int AUTOMATIC_STORAGE_MANAGER_DAYS_TO_RETAIN_DEFAULT = 90;
/**
+ * How many bytes the automatic storage manager has cleared out.
+ *
+ * @hide
+ */
+ public static final String AUTOMATIC_STORAGE_MANAGER_BYTES_CLEARED =
+ "automatic_storage_manager_bytes_cleared";
+
+
+ /**
+ * Last run time for the automatic storage manager.
+ *
+ * @hide
+ */
+ public static final String AUTOMATIC_STORAGE_MANAGER_LAST_RUN =
+ "automatic_storage_manager_last_run";
+
+
+ /**
* This are the settings to be backed up.
*
* NOTE: Settings are backed up and restored in the order they appear
diff --git a/core/java/android/util/ArraySet.java b/core/java/android/util/ArraySet.java
index d39e91f..1e765b6 100644
--- a/core/java/android/util/ArraySet.java
+++ b/core/java/android/util/ArraySet.java
@@ -402,11 +402,14 @@
throw new IllegalStateException("Array is full");
}
if (index > 0 && mHashes[index - 1] > hash) {
- RuntimeException e = new RuntimeException("here");
- e.fillInStackTrace();
- Log.w(TAG, "New hash " + hash
- + " is before end of array hash " + mHashes[index - 1]
- + " at index " + index, e);
+ // Cannot optimize since it would break the sorted order - fallback to add()
+ if (DEBUG) {
+ RuntimeException e = new RuntimeException("here");
+ e.fillInStackTrace();
+ Log.w(TAG, "New hash " + hash
+ + " is before end of array hash " + mHashes[index - 1]
+ + " at index " + index, e);
+ }
add(value);
return;
}
diff --git a/core/res/res/values/colors_device_defaults.xml b/core/res/res/values/colors_device_defaults.xml
index 5518de2..f33eb6f 100644
--- a/core/res/res/values/colors_device_defaults.xml
+++ b/core/res/res/values/colors_device_defaults.xml
@@ -26,6 +26,7 @@
<color name="secondary_device_default_settings">@color/secondary_material_settings</color>
<color name="tertiary_device_default_settings">@color/tertiary_material_settings</color>
+ <color name="quaternary_device_default_settings">@color/quaternary_material_settings</color>
<color name="accent_device_default_light">@color/accent_material_light</color>
<color name="accent_device_default_dark">@color/accent_material_dark</color>
diff --git a/core/res/res/values/colors_material.xml b/core/res/res/values/colors_material.xml
index 2ac4092a5..8a6c229 100644
--- a/core/res/res/values/colors_material.xml
+++ b/core/res/res/values/colors_material.xml
@@ -34,6 +34,7 @@
<color name="secondary_material_settings">@color/material_blue_grey_800</color>
<color name="tertiary_material_settings">@color/material_blue_grey_700</color>
+ <color name="quaternary_material_settings">@color/material_blue_grey_200</color>
<color name="accent_material_light">@color/material_deep_teal_500</color>
<color name="accent_material_dark">@color/material_deep_teal_200</color>
@@ -85,6 +86,7 @@
<color name="material_deep_teal_300">#ff4db6ac</color>
<color name="material_deep_teal_500">#ff009688</color>
+ <color name="material_blue_grey_200">#ffb0bec5</color>
<color name="material_blue_grey_700">#ff455a64</color>
<color name="material_blue_grey_800">#ff37474f</color>
<color name="material_blue_grey_900">#ff263238</color>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 1da59c9..7d8d811 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2493,6 +2493,9 @@
<string-array translatable="false" name="config_defaultPinnerServiceFiles">
</string-array>
+ <!-- True if camera app should be pinned via Pinner Service -->
+ <bool name="config_pinnerCameraApp">false</bool>
+
<!-- Component that is the default launcher when demo mode is enabled. -->
<string name="config_demoModeLauncherComponent">com.android.retaildemo/.DemoPlayer</string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 96d7394..d426d1a 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2619,6 +2619,7 @@
<!-- Pinner Service -->
<java-symbol type="array" name="config_defaultPinnerServiceFiles" />
+ <java-symbol type="bool" name="config_pinnerCameraApp" />
<java-symbol type="string" name="config_doubleTouchGestureEnableFile" />
diff --git a/docs/html/sdk/sdk_vars.cs b/docs/html/sdk/sdk_vars.cs
index 3d6f058..af13043 100644
--- a/docs/html/sdk/sdk_vars.cs
+++ b/docs/html/sdk/sdk_vars.cs
@@ -1,22 +1,22 @@
<?cs
-set:ndk.mac64_download='android-ndk-r12-darwin-x86_64.zip' ?><?cs
-set:ndk.mac64_bytes='734014148' ?><?cs
-set:ndk.mac64_checksum='708d4025142924f7097a9f44edf0a35965706737' ?><?cs
+set:ndk.mac64_download='android-ndk-r12b-darwin-x86_64.zip' ?><?cs
+set:ndk.mac64_bytes='734135279' ?><?cs
+set:ndk.mac64_checksum='e257fe12f8947be9f79c10c3fffe87fb9406118a' ?><?cs
-set:ndk.linux64_download='android-ndk-r12-linux-x86_64.zip' ?><?cs
-set:ndk.linux64_bytes='755431993' ?><?cs
-set:ndk.linux64_checksum='b7e02dc733692447366a2002ad17e87714528b39' ?><?cs
+set:ndk.linux64_download='android-ndk-r12b-linux-x86_64.zip' ?><?cs
+set:ndk.linux64_bytes='755551010' ?><?cs
+set:ndk.linux64_checksum='170a119bfa0f0ce5dc932405eaa3a7cc61b27694' ?><?cs
-set:ndk.win32_download='android-ndk-r12-windows-x86.zip' ?><?cs
-set:ndk.win32_bytes='706332762' ?><?cs
-set:ndk.win32_checksum='37fcd7acf6012d0068a57c1524edf24b0fef69c9' ?><?cs
+set:ndk.win32_download='android-ndk-r12b-windows-x86.zip' ?><?cs
+set:ndk.win32_bytes='706453972' ?><?cs
+set:ndk.win32_checksum='8e6eef0091dac2f3c7a1ecbb7070d4fa22212c04' ?><?cs
-set:ndk.win64_download='android-ndk-r12-windows-x86_64.zip' ?><?cs
-set:ndk.win64_bytes='749444245' ?><?cs
-set:ndk.win64_checksum='80d64a77aab52df867ac55cec1e976663dd3326f'
+set:ndk.win64_download='android-ndk-r12b-windows-x86_64.zip' ?><?cs
+set:ndk.win64_bytes='749567353' ?><?cs
+set:ndk.win64_checksum='337746d8579a1c65e8a69bf9cbdc9849bcacf7f5'
?>
<?cs
def:size_in_mb(bytes)
?><?cs set:mb = bytes / 1024 / 1024
?><?cs var:mb ?><?cs
-/def ?>
+/def ?>
\ No newline at end of file
diff --git a/graphics/java/android/graphics/drawable/VectorDrawable.java b/graphics/java/android/graphics/drawable/VectorDrawable.java
index c34f474..4f6368c 100644
--- a/graphics/java/android/graphics/drawable/VectorDrawable.java
+++ b/graphics/java/android/graphics/drawable/VectorDrawable.java
@@ -243,9 +243,7 @@
* constructors to set the state and initialize local properties.
*/
private VectorDrawable(@NonNull VectorDrawableState state, @Nullable Resources res) {
- // Constant state sharing is disabled until we fix onStateChanged()
- // affecting the shared bitmap.
- mVectorState = new VectorDrawableState(state);
+ mVectorState = state;
updateLocalState(res);
}
@@ -391,6 +389,11 @@
protected boolean onStateChange(int[] stateSet) {
boolean changed = false;
+ // When the VD is stateful, we need to mutate the drawable such that we don't share the
+ // cache bitmap with others. Such that the state change only affect this new cached bitmap.
+ if (isStateful()) {
+ mutate();
+ }
final VectorDrawableState state = mVectorState;
if (state.onStateChange(stateSet)) {
changed = true;
diff --git a/packages/DocumentsUI/src/com/android/documentsui/QuickViewIntentBuilder.java b/packages/DocumentsUI/src/com/android/documentsui/QuickViewIntentBuilder.java
index af6aee7..5e3bbbb 100644
--- a/packages/DocumentsUI/src/com/android/documentsui/QuickViewIntentBuilder.java
+++ b/packages/DocumentsUI/src/com/android/documentsui/QuickViewIntentBuilder.java
@@ -28,6 +28,7 @@
import android.content.res.Resources;
import android.database.Cursor;
import android.net.Uri;
+import android.os.Build;
import android.provider.DocumentsContract;
import android.provider.DocumentsContract.Document;
import android.support.annotation.Nullable;
@@ -73,7 +74,7 @@
@Nullable Intent build() {
if (DEBUG) Log.d(TAG, "Preparing intent for doc:" + mDocument.documentId);
- String trustedPkg = mResources.getString(R.string.trusted_quick_viewer_package);
+ String trustedPkg = getQuickViewPackage();
if (!TextUtils.isEmpty(trustedPkg)) {
Intent intent = new Intent(Intent.ACTION_QUICK_VIEW);
@@ -116,6 +117,16 @@
return null;
}
+ private String getQuickViewPackage() {
+ String resValue = mResources.getString(R.string.trusted_quick_viewer_package);
+ if (Build.IS_DEBUGGABLE ) {
+ // Allow users of debug devices to override default quick viewer
+ // for the purposes of testing.
+ return android.os.SystemProperties.get("debug.quick_viewer", resValue);
+ }
+ return resValue;
+ }
+
private int collectViewableUris(ArrayList<Uri> uris) {
final String[] siblingIds = mModel.getModelIds();
uris.ensureCapacity(siblingIds.length);
diff --git a/packages/SystemUI/res/color/qs_detail_empty.xml b/packages/SystemUI/res/color/qs_detail_empty.xml
new file mode 100644
index 0000000..4be39c7
--- /dev/null
+++ b/packages/SystemUI/res/color/qs_detail_empty.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:alpha="0.14" android:color="@*android:color/quaternary_device_default_settings" />
+</selector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index d12ef42..9061376 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -34,8 +34,7 @@
<color name="qs_text">#FFFFFFFF</color>
<color name="qs_tile_divider">#29ffffff</color><!-- 16% white -->
<color name="qs_subhead">#99FFFFFF</color><!-- 60% white -->
- <color name="qs_detail_empty">#24B0BEC5</color><!-- 14% blue grey 200 -->
- <color name="qs_detail_button">#FFB0BEC5</color><!-- 100% blue grey 200 -->
+ <color name="qs_detail_button">@*android:color/quaternary_device_default_settings</color>
<color name="qs_detail_button_white">#B3FFFFFF</color><!-- 70% white -->
<color name="qs_detail_transition">#66FFFFFF</color>
<color name="data_usage_secondary">#99FFFFFF</color><!-- 60% white -->
@@ -122,7 +121,7 @@
<color name="segmented_buttons_background">#14FFFFFF</color><!-- 8% white -->
<color name="segmented_button_selected">#FFFFFFFF</color>
- <color name="segmented_button_unselected">#FFB0BEC5</color><!-- blue grey 200 -->
+ <color name="segmented_button_unselected">@*android:color/quaternary_device_default_settings</color>
<color name="dark_mode_icon_color_single_tone">#99000000</color>
<color name="dark_mode_icon_color_dual_tone_background">#3d000000</color>
@@ -134,7 +133,7 @@
<color name="volume_icon_color">#ffffffff</color>
<color name="volume_settings_icon_color">#7fffffff</color>
- <color name="volume_slider_inactive">#FFB0BEC5</color><!-- blue grey 200 -->
+ <color name="volume_slider_inactive">@*android:color/quaternary_device_default_settings</color>
<color name="docked_divider_background">#ff000000</color>
<color name="docked_divider_handle">#ffffff</color>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index e5681ce..1ee13e9 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -290,7 +290,7 @@
<style name="TextAppearance.Volume.ZenDetail">
<item name="android:textSize">14sp</item>
<item name="android:fontFamily">sans-serif</item>
- <item name="android:textColor">#ffb0b3c5</item>
+ <item name="android:textColor">@*android:color/quaternary_device_default_settings</item>
</style>
<style name="VolumeButtons" parent="@android:style/Widget.Material.Button.Borderless">
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
index 379ad53..27ba003 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
@@ -431,23 +431,27 @@
}
private void listenForCallState() {
- TelephonyManager.from(mContext).listen(new PhoneStateListener() {
- private int mCallState;
- @Override
- public void onCallStateChanged(int state, String incomingNumber) {
- if (mCallState == state) return;
- if (DEBUG) Log.v(TAG, "Call state changed: " + state);
- mCallState = state;
- int currentUserId = ActivityManager.getCurrentUser();
- UserInfo userInfo = mUserManager.getUserInfo(currentUserId);
- if (userInfo != null && userInfo.isGuest()) {
- showGuestNotification(currentUserId);
- }
- refreshUsers(UserHandle.USER_NULL);
- }
- }, PhoneStateListener.LISTEN_CALL_STATE);
+ TelephonyManager.from(mContext).listen(mPhoneStateListener,
+ PhoneStateListener.LISTEN_CALL_STATE);
}
+ private final PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
+ private int mCallState;
+
+ @Override
+ public void onCallStateChanged(int state, String incomingNumber) {
+ if (mCallState == state) return;
+ if (DEBUG) Log.v(TAG, "Call state changed: " + state);
+ mCallState = state;
+ int currentUserId = ActivityManager.getCurrentUser();
+ UserInfo userInfo = mUserManager.getUserInfo(currentUserId);
+ if (userInfo != null && userInfo.isGuest()) {
+ showGuestNotification(currentUserId);
+ }
+ refreshUsers(UserHandle.USER_NULL);
+ }
+ };
+
private BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/BatteryPreference.java b/packages/SystemUI/src/com/android/systemui/tuner/BatteryPreference.java
index 8881c79..6e08139 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/BatteryPreference.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/BatteryPreference.java
@@ -47,9 +47,9 @@
@Override
public void onAttached() {
super.onAttached();
- TunerService.get(getContext()).addTunable(this, StatusBarIconController.ICON_BLACKLIST);
mHasPercentage = Settings.System.getInt(getContext().getContentResolver(),
SHOW_PERCENT_SETTING, 0) != 0;
+ TunerService.get(getContext()).addTunable(this, StatusBarIconController.ICON_BLACKLIST);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/ClockPreference.java b/packages/SystemUI/src/com/android/systemui/tuner/ClockPreference.java
index ea92443..caa0527 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/ClockPreference.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/ClockPreference.java
@@ -32,6 +32,8 @@
private boolean mHasSeconds;
private ArraySet<String> mBlacklist;
private boolean mHasSetValue;
+ private boolean mReceivedSeconds;
+ private boolean mReceivedClock;
public ClockPreference(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -55,12 +57,14 @@
@Override
public void onTuningChanged(String key, String newValue) {
if (StatusBarIconController.ICON_BLACKLIST.equals(key)) {
+ mReceivedClock = true;
mBlacklist = StatusBarIconController.getIconBlacklist(newValue);
mClockEnabled = !mBlacklist.contains(mClock);
} else if (Clock.CLOCK_SECONDS.equals(key)) {
+ mReceivedSeconds = true;
mHasSeconds = newValue != null && Integer.parseInt(newValue) != 0;
}
- if (!mHasSetValue) {
+ if (!mHasSetValue && mReceivedClock && mReceivedSeconds) {
// Because of the complicated tri-state it can end up looping and setting state back to
// what the user didn't choose. To avoid this, just set the state once and rely on the
// preference to handle updates.
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index e0d89f2..71ac544 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -177,6 +177,8 @@
static final int MSG_HARD_KEYBOARD_SWITCH_CHANGED = 4000;
+ static final int MSG_SYSTEM_UNLOCK_USER = 5000;
+
static final long TIME_TO_RECONNECT = 3 * 1000;
static final int SECURE_SUGGESTION_SPANS_MAX_SIZE = 20;
@@ -800,14 +802,14 @@
@Override
public void onSwitchUser(@UserIdInt int userHandle) {
- // Called on the system server's main looper thread.
+ // Called on ActivityManager thread.
// TODO: Dispatch this to a worker thread as needed.
mService.onSwitchUser(userHandle);
}
@Override
public void onBootPhase(int phase) {
- // Called on the system server's main looper thread.
+ // Called on ActivityManager thread.
// TODO: Dispatch this to a worker thread as needed.
if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
StatusBarManagerService statusBarService = (StatusBarManagerService) ServiceManager
@@ -817,10 +819,10 @@
}
@Override
- public void onUnlockUser(@UserIdInt int userHandle) {
- // Called on the system server's main looper thread.
- // TODO: Dispatch this to a worker thread as needed.
- mService.onUnlockUser(userHandle);
+ public void onUnlockUser(final @UserIdInt int userHandle) {
+ // Called on ActivityManager thread.
+ mService.mHandler.sendMessage(mService.mHandler.obtainMessage(MSG_SYSTEM_UNLOCK_USER,
+ userHandle));
}
}
@@ -2970,6 +2972,10 @@
case MSG_HARD_KEYBOARD_SWITCH_CHANGED:
mHardKeyboardListener.handleHardKeyboardStatusChange(msg.arg1 == 1);
return true;
+ case MSG_SYSTEM_UNLOCK_USER:
+ final int userId = msg.arg1;
+ onUnlockUser(userId);
+ return true;
}
return false;
}
diff --git a/services/core/java/com/android/server/PinnerService.java b/services/core/java/com/android/server/PinnerService.java
index d48aeed..e63f536 100644
--- a/services/core/java/com/android/server/PinnerService.java
+++ b/services/core/java/com/android/server/PinnerService.java
@@ -17,18 +17,29 @@
package com.android.server;
import android.content.Context;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
import android.content.res.Resources;
+import android.content.Intent;
import android.util.EventLog;
import android.util.Slog;
import android.os.Binder;
import android.os.Build;
+import android.provider.MediaStore;
import android.system.ErrnoException;
import android.system.Os;
import android.system.OsConstants;
import android.system.StructStat;
+import com.android.internal.app.ResolverActivity;
+
+import dalvik.system.VMRuntime;
+
import java.util.ArrayList;
+import java.util.List;
import java.io.FileDescriptor;
import java.io.FileOutputStream;
import java.io.IOException;
@@ -37,80 +48,268 @@
/**
* <p>PinnerService pins important files for key processes in memory.</p>
* <p>Files to pin are specified in the config_defaultPinnerServiceFiles
- * overlay. </p>
+ * overlay.</p>
+ * <p>Pin the default camera application if specified in config_pinnerCameraApp.</p>
*/
public final class PinnerService extends SystemService {
private static final boolean DEBUG = false;
private static final String TAG = "PinnerService";
private final Context mContext;
- private final ArrayList<String> mPinnedFiles = new ArrayList<String>();
+ private final ArrayList<PinnedFile> mPinnedFiles = new ArrayList<PinnedFile>();
+ private final ArrayList<PinnedFile> mPinnedCameraFiles = new ArrayList<PinnedFile>();
+ private final boolean mShouldPinCamera;
private BinderService mBinderService;
+ private final long MAX_CAMERA_PIN_SIZE = 50 * (1 << 20); //50MB max
+
public PinnerService(Context context) {
super(context);
mContext = context;
-
+ mShouldPinCamera = context.getResources().getBoolean(
+ com.android.internal.R.bool.config_pinnerCameraApp);
}
@Override
public void onStart() {
- Slog.e(TAG, "Starting PinnerService");
-
+ if (DEBUG) {
+ Slog.i(TAG, "Starting PinnerService");
+ }
mBinderService = new BinderService();
publishBinderService("pinner", mBinderService);
// Files to pin come from the overlay and can be specified per-device config
+ String[] filesToPin = mContext.getResources().getStringArray(
+ com.android.internal.R.array.config_defaultPinnerServiceFiles);
// Continue trying to pin remaining files even if there is a failure
- String[] filesToPin = mContext.getResources().getStringArray(com.android.internal.R.array.config_defaultPinnerServiceFiles);
for (int i = 0; i < filesToPin.length; i++){
- boolean success = pinFile(filesToPin[i], 0, 0);
- if (success == true) {
- mPinnedFiles.add(filesToPin[i]);
- Slog.i(TAG, "Pinned file = " + filesToPin[i]);
+ PinnedFile pf = pinFile(filesToPin[i], 0, 0, 0);
+ if (pf != null) {
+ mPinnedFiles.add(pf);
+ if (DEBUG) {
+ Slog.i(TAG, "Pinned file = " + pf.mFilename);
+ }
} else {
Slog.e(TAG, "Failed to pin file = " + filesToPin[i]);
}
}
}
- // mlock length bytes of fileToPin in memory, starting at offset
- // length == 0 means pin from offset to end of file
- private boolean pinFile(String fileToPin, long offset, long length) {
+ /**
+ * Pin camera on unlock.
+ * We have to wait for unlock because the user's
+ * preference for camera is not available from PackageManager until after
+ * unlock
+ */
+ @Override
+ public void onUnlockUser(int userHandle) {
+ handlePin(userHandle);
+ }
+
+ /**
+ * Pin camera on user switch.
+ * If more than one user is using the device
+ * each user may set a different preference for the camera app.
+ * Make sure that user's preference is pinned into memory.
+ */
+ @Override
+ public void onSwitchUser(int userHandle) {
+ handlePin(userHandle);
+ }
+
+ private void handlePin(int userHandle) {
+ if (mShouldPinCamera) {
+ boolean success = pinCamera(userHandle);
+ if (!success) {
+ //this is not necessarily an error
+ if (DEBUG) {
+ Slog.v(TAG, "Failed to pin camera.");
+ }
+ }
+ }
+ }
+
+ /**
+ * determine if the camera app is already pinned by comparing the
+ * intent resolution to the pinned files list
+ */
+ private boolean alreadyPinned(int userHandle) {
+ ApplicationInfo cameraInfo = getCameraInfo(userHandle);
+ if (cameraInfo == null ) {
+ return false;
+ }
+ for (int i = 0; i < mPinnedCameraFiles.size(); i++) {
+ if (mPinnedCameraFiles.get(i).mFilename.equals(cameraInfo.sourceDir)) {
+ if (DEBUG) {
+ Slog.v(TAG, "Camera is already pinned");
+ }
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private void unpinCameraApp() {
+ for (int i = 0; i < mPinnedCameraFiles.size(); i++) {
+ unpinFile(mPinnedCameraFiles.get(i));
+ }
+ mPinnedCameraFiles.clear();
+ }
+
+ private boolean isResolverActivity(ActivityInfo info) {
+ return ResolverActivity.class.getName().equals(info.name);
+ }
+
+ private ApplicationInfo getCameraInfo(int userHandle) {
+ // find the camera via an intent
+ // use INTENT_ACTION_STILL_IMAGE_CAMERA instead of _SECURE. On a
+ // device without a fbe enabled, the _SECURE intent will never get set.
+ Intent cameraIntent = new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA);
+ PackageManager pm = mContext.getPackageManager();
+ ResolveInfo cameraResolveInfo = pm.resolveActivityAsUser(
+ cameraIntent, PackageManager.MATCH_DEFAULT_ONLY, userHandle);
+ if (cameraResolveInfo == null ) {
+ //this is not necessarily an error
+ if (DEBUG) {
+ Slog.v(TAG, "Unable to resolve camera intent");
+ }
+ return null;
+ }
+
+ if (isResolverActivity(cameraResolveInfo.activityInfo))
+ {
+ return null;
+ }
+
+ return cameraResolveInfo.activityInfo.applicationInfo;
+ }
+
+ private boolean pinCamera(int userHandle){
+ //we may have already pinned a camera app. If we've pinned this
+ //camera app, we're done. otherwise, unpin and pin the new app
+ if (alreadyPinned(userHandle)){
+ return true;
+ }
+
+ ApplicationInfo cameraInfo = getCameraInfo(userHandle);
+ if (cameraInfo == null) {
+ return false;
+ }
+
+ //unpin after checking that the camera intent has resolved
+ //this prevents us from thrashing when switching users with
+ //FBE enabled, because the intent won't resolve until the unlock
+ unpinCameraApp();
+
+ //pin APK
+ String camAPK = cameraInfo.sourceDir;
+ PinnedFile pf = pinFile(camAPK, 0, 0, MAX_CAMERA_PIN_SIZE);
+ if (pf == null) {
+ Slog.e(TAG, "Failed to pin " + camAPK);
+ return false;
+ }
+ if (DEBUG) {
+ Slog.i(TAG, "Pinned " + pf.mFilename);
+ }
+ mPinnedCameraFiles.add(pf);
+
+ //find the location of the odex based on the location of the APK
+ int lastPeriod = camAPK.lastIndexOf('.');
+ int lastSlash = camAPK.lastIndexOf('/', lastPeriod);
+ String base = camAPK.substring(0, lastSlash);
+ String appName = camAPK.substring(lastSlash + 1, lastPeriod);
+
+ // determine the ABI from either ApplicationInfo or Build
+ String arch = "arm";
+ if (cameraInfo.primaryCpuAbi != null
+ && VMRuntime.is64BitAbi(cameraInfo.primaryCpuAbi)) {
+ arch = arch + "64";
+ } else {
+ if (VMRuntime.is64BitAbi(Build.SUPPORTED_ABIS[0])) {
+ arch = arch + "64";
+ }
+ }
+ String odex = base + "/oat/" + arch + "/" + appName + ".odex";
+ //not all apps have odex files, so not pinning the odex is not a fatal error
+ pf = pinFile(odex, 0, 0, MAX_CAMERA_PIN_SIZE);
+ if (pf != null) {
+ mPinnedCameraFiles.add(pf);
+ if (DEBUG) {
+ Slog.i(TAG, "Pinned " + pf.mFilename);
+ }
+ }
+ return true;
+ }
+
+
+ /** mlock length bytes of fileToPin in memory, starting at offset
+ * length == 0 means pin from offset to end of file
+ * maxSize == 0 means infinite
+ */
+ private static PinnedFile pinFile(String fileToPin, long offset, long length, long maxSize) {
FileDescriptor fd = new FileDescriptor();
try {
- fd = Os.open(fileToPin, OsConstants.O_RDONLY | OsConstants.O_CLOEXEC | OsConstants.O_NOFOLLOW, OsConstants.O_RDONLY);
+ fd = Os.open(fileToPin,
+ OsConstants.O_RDONLY | OsConstants.O_CLOEXEC | OsConstants.O_NOFOLLOW,
+ OsConstants.O_RDONLY);
StructStat sb = Os.fstat(fd);
if (offset + length > sb.st_size) {
Os.close(fd);
- return false;
+ Slog.e(TAG, "Failed to pin file " + fileToPin +
+ ", request extends beyond end of file. offset + length = "
+ + (offset + length) + ", file length = " + sb.st_size);
+ return null;
}
if (length == 0) {
length = sb.st_size - offset;
}
- long address = Os.mmap(0, length, OsConstants.PROT_READ, OsConstants.MAP_PRIVATE, fd, offset);
+ if (maxSize > 0 && length > maxSize) {
+ Slog.e(TAG, "Could not pin file " + fileToPin +
+ ", size = " + length + ", maxSize = " + maxSize);
+ Os.close(fd);
+ return null;
+ }
+
+ long address = Os.mmap(0, length, OsConstants.PROT_READ,
+ OsConstants.MAP_PRIVATE, fd, offset);
Os.close(fd);
Os.mlock(address, length);
- return true;
+ return new PinnedFile(address, length, fileToPin);
} catch (ErrnoException e) {
- Slog.e(TAG, "Failed to pin file " + fileToPin + " with error " + e.getMessage());
+ Slog.e(TAG, "Could not pin file " + fileToPin + " with error " + e.getMessage());
if(fd.valid()) {
- try { Os.close(fd); }
- catch (ErrnoException eClose) {Slog.e(TAG, "Failed to close fd, error = " + eClose.getMessage());}
+ try {
+ Os.close(fd);
+ }
+ catch (ErrnoException eClose) {
+ Slog.e(TAG, "Failed to close fd, error = " + eClose.getMessage());
+ }
}
- return false;
+ return null;
}
}
+ private static boolean unpinFile(PinnedFile pf) {
+ try {
+ Os.munlock(pf.mAddress, pf.mLength);
+ } catch (ErrnoException e) {
+ Slog.e(TAG, "Failed to unpin file " + pf.mFilename + " with error " + e.getMessage());
+ return false;
+ }
+ if (DEBUG) {
+ Slog.i(TAG, "Unpinned file " + pf.mFilename );
+ }
+ return true;
+ }
private final class BinderService extends Binder {
@Override
@@ -118,8 +317,23 @@
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
pw.println("Pinned Files:");
for (int i = 0; i < mPinnedFiles.size(); i++) {
- pw.println(mPinnedFiles.get(i));
+ pw.println(mPinnedFiles.get(i).mFilename);
}
+ for (int i = 0; i < mPinnedCameraFiles.size(); i++) {
+ pw.println(mPinnedCameraFiles.get(i).mFilename);
+ }
+ }
+ }
+
+ private static class PinnedFile {
+ long mAddress;
+ long mLength;
+ String mFilename;
+
+ PinnedFile(long address, long length, String filename) {
+ mAddress = address;
+ mLength = length;
+ mFilename = filename;
}
}
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index ae9492f..e5579e2 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -21724,6 +21724,33 @@
updateConfigurationLocked(values, null, false, true, userId);
}
}
+
+ @Override
+ public IIntentSender getActivityIntentSenderAsPackage(
+ String packageName, int userId, int requestCode, Intent intent,
+ int flags, Bundle bOptions) {
+ String resolvedType = intent != null ? intent.resolveTypeIfNeeded(
+ mContext.getContentResolver()) : null;
+
+ // UID of the package on user userId.
+ // "= 0" is needed because otherwise catch(RemoteException) would make it look like
+ // packageUid may not be initialized.
+ int packageUid = 0;
+ try {
+ packageUid = AppGlobals.getPackageManager().getPackageUid(
+ packageName, PackageManager.MATCH_DEBUG_TRIAGED_MISSING, userId);
+ } catch (RemoteException e) {
+ // Shouldn't happen.
+ }
+
+ synchronized (ActivityManagerService.this) {
+ return getIntentSenderLocked(
+ ActivityManager.INTENT_SENDER_ACTIVITY, packageName, packageUid,
+ UserHandle.getUserId(packageUid), /*token*/ null, /*resultWho*/ null,
+ requestCode, new Intent[] {intent}, new String[]{resolvedType},
+ flags, bOptions);
+ }
+ }
}
private final class SleepTokenImpl extends SleepToken {
diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java
index 46da607..03d5645f 100644
--- a/services/core/java/com/android/server/pm/LauncherAppsService.java
+++ b/services/core/java/com/android/server/pm/LauncherAppsService.java
@@ -19,9 +19,13 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
+import android.app.ActivityManagerInternal;
+import android.app.ActivityManagerNative;
import android.app.AppGlobals;
+import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Context;
+import android.content.IIntentSender;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
@@ -98,6 +102,7 @@
private final Context mContext;
private final PackageManager mPm;
private final UserManager mUm;
+ private final ActivityManagerInternal mActivityManagerInternal;
private final ShortcutServiceInternal mShortcutServiceInternal;
private final PackageCallbackList<IOnAppsChangedListener> mListeners
= new PackageCallbackList<IOnAppsChangedListener>();
@@ -110,6 +115,8 @@
mContext = context;
mPm = mContext.getPackageManager();
mUm = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+ mActivityManagerInternal = Preconditions.checkNotNull(
+ LocalServices.getService(ActivityManagerInternal.class));
mShortcutServiceInternal = Preconditions.checkNotNull(
LocalServices.getService(ShortcutServiceInternal.class));
mShortcutServiceInternal.addListener(mPackageMonitor);
@@ -432,7 +439,7 @@
}
@Override
- public boolean startShortcut(String callingPackage, String packageName, String shortcutId,
+ public void startShortcut(String callingPackage, String packageName, String shortcutId,
Rect sourceBounds, Bundle startActivityOptions, int userId) {
verifyCallingPackage(callingPackage);
ensureInUserProfiles(userId, "Cannot start activity for unrelated profile " + userId);
@@ -451,20 +458,40 @@
final Intent intent = mShortcutServiceInternal.createShortcutIntent(getCallingUserId(),
callingPackage, packageName, shortcutId, userId);
if (intent == null) {
- return false;
+ return;
}
// Note the target activity doesn't have to be exported.
- intent.setSourceBounds(sourceBounds);
prepareIntentForLaunch(intent, sourceBounds);
- final long ident = Binder.clearCallingIdentity();
+ startShortcutIntentAsPublisher(
+ intent, packageName, startActivityOptions, userId);
+ }
+
+ @VisibleForTesting
+ protected void startShortcutIntentAsPublisher(@NonNull Intent intent,
+ @NonNull String publisherPackage, Bundle startActivityOptions, int userId) {
+
try {
- mContext.startActivityAsUser(intent, startActivityOptions, UserHandle.of(userId));
- } finally {
- Binder.restoreCallingIdentity(ident);
+ final IIntentSender intentSender;
+
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ intentSender = mActivityManagerInternal.getActivityIntentSenderAsPackage(
+ publisherPackage, userId, /* requestCode= */ 0,
+ intent, PendingIntent.FLAG_ONE_SHOT,
+ /* options= */ startActivityOptions);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+
+ // Negative result means a failure.
+ ActivityManagerNative.getDefault().sendIntentSender(
+ intentSender, 0, null, null, null, null, null);
+
+ } catch (RemoteException e) {
+ return;
}
- return true;
}
@Override
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 114ad96..2ebb9f6 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -3216,8 +3216,12 @@
final PermissionsState permissionsState = ps.getPermissionsState();
- final int[] gids = permissionsState.computeGids(userId);
- final Set<String> permissions = permissionsState.getPermissions(userId);
+ // Compute GIDs only if requested
+ final int[] gids = (flags & PackageManager.GET_GIDS) == 0
+ ? EMPTY_INT_ARRAY : permissionsState.computeGids(userId);
+ // Compute granted permissions only if package has requested permissions
+ final Set<String> permissions = ArrayUtils.isEmpty(p.requestedPermissions)
+ ? Collections.<String>emptySet() : permissionsState.getPermissions(userId);
final PackageUserState state = ps.readUserState(userId);
return PackageParser.generatePackageInfo(p, gids, flags,
diff --git a/services/core/java/com/android/server/pm/PermissionsState.java b/services/core/java/com/android/server/pm/PermissionsState.java
index 007b738..8f9968ec 100644
--- a/services/core/java/com/android/server/pm/PermissionsState.java
+++ b/services/core/java/com/android/server/pm/PermissionsState.java
@@ -274,7 +274,7 @@
return Collections.emptySet();
}
- Set<String> permissions = new ArraySet<>();
+ Set<String> permissions = new ArraySet<>(mPermissions.size());
final int permissionCount = mPermissions.size();
for (int i = 0; i < permissionCount; i++) {
@@ -282,6 +282,7 @@
if (hasInstallPermission(permission)) {
permissions.add(permission);
+ continue;
}
if (userId != UserHandle.USER_ALL) {
diff --git a/services/core/java/com/android/server/search/SearchManagerService.java b/services/core/java/com/android/server/search/SearchManagerService.java
index 4d91814..2e5eb3a 100644
--- a/services/core/java/com/android/server/search/SearchManagerService.java
+++ b/services/core/java/com/android/server/search/SearchManagerService.java
@@ -33,6 +33,7 @@
import android.database.ContentObserver;
import android.os.Binder;
import android.os.Bundle;
+import android.os.Handler;
import android.os.RemoteException;
import android.os.UserHandle;
import android.os.UserManager;
@@ -42,6 +43,7 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.content.PackageMonitor;
+import com.android.internal.os.BackgroundThread;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.LocalServices;
import com.android.server.SystemService;
@@ -57,6 +59,7 @@
*/
public class SearchManagerService extends ISearchManager.Stub {
private static final String TAG = "SearchManagerService";
+ final Handler mHandler;
public static class Lifecycle extends SystemService {
private SearchManagerService mService;
@@ -72,8 +75,13 @@
}
@Override
- public void onUnlockUser(int userHandle) {
- mService.onUnlockUser(userHandle);
+ public void onUnlockUser(final int userId) {
+ mService.mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mService.onUnlockUser(userId);
+ }
+ });
}
@Override
@@ -99,6 +107,7 @@
mContext = context;
new MyPackageMonitor().register(context, null, UserHandle.ALL, true);
new GlobalSearchProviderObserver(context.getContentResolver());
+ mHandler = BackgroundThread.getHandler();
}
private Searchables getSearchables(int userId) {
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 7c2ba1e..79fe18b 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -229,10 +229,12 @@
if (moved && lockWallpaperChanged) {
// We just migrated sys -> lock to preserve imagery for an impending
- // new system-only wallpaper. Tell keyguard about it but that's it.
+ // new system-only wallpaper. Tell keyguard about it and make sure it
+ // has the right SELinux label.
if (DEBUG) {
Slog.i(TAG, "Sys -> lock MOVED_TO");
}
+ SELinux.restorecon(changedFile);
notifyLockWallpaperChanged();
return;
}
@@ -254,9 +256,11 @@
if (moved) {
// This is a restore, so generate the crop using any just-restored new
// crop guidelines, making sure to preserve our local dimension hints.
+ // We also make sure to reapply the correct SELinux label.
if (DEBUG) {
Slog.v(TAG, "moved-to, therefore restore; reloading metadata");
}
+ SELinux.restorecon(changedFile);
loadSettingsLocked(wallpaper.userId, true);
}
generateCrop(wallpaper);
diff --git a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
index 7cf03af..b6084d5 100644
--- a/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/BaseShortcutManagerTest.java
@@ -37,6 +37,7 @@
import android.annotation.RequiresPermission;
import android.annotation.UserIdInt;
import android.app.Activity;
+import android.app.ActivityManagerInternal;
import android.app.IUidObserver;
import android.app.usage.UsageStatsManagerInternal;
import android.content.BroadcastReceiver;
@@ -466,6 +467,13 @@
void injectRestoreCallingIdentity(long token) {
mInjectedCallingUid = (int) token;
}
+
+ @Override
+ protected void startShortcutIntentAsPublisher(@NonNull Intent intent,
+ @NonNull String publisherPackage, Bundle startActivityOptions, int userId) {
+ // Just forward to startActivityAsUser() during unit tests.
+ mContext.startActivityAsUser(intent, startActivityOptions, UserHandle.of(userId));
+ }
}
protected class LauncherAppsTestable extends LauncherApps {
@@ -518,6 +526,7 @@
protected PackageManagerInternal mMockPackageManagerInternal;
protected UserManager mMockUserManager;
protected UsageStatsManagerInternal mMockUsageStatsManagerInternal;
+ protected ActivityManagerInternal mMockActivityManagerInternal;
protected static final String CALLING_PACKAGE_1 = "com.android.test.1";
protected static final int CALLING_UID_1 = 10001;
@@ -616,11 +625,14 @@
mMockPackageManagerInternal = mock(PackageManagerInternal.class);
mMockUserManager = mock(UserManager.class);
mMockUsageStatsManagerInternal = mock(UsageStatsManagerInternal.class);
+ mMockActivityManagerInternal = mock(ActivityManagerInternal.class);
LocalServices.removeServiceForTest(PackageManagerInternal.class);
LocalServices.addService(PackageManagerInternal.class, mMockPackageManagerInternal);
LocalServices.removeServiceForTest(UsageStatsManagerInternal.class);
LocalServices.addService(UsageStatsManagerInternal.class, mMockUsageStatsManagerInternal);
+ LocalServices.removeServiceForTest(ActivityManagerInternal.class);
+ LocalServices.addService(ActivityManagerInternal.class, mMockActivityManagerInternal);
// Prepare injection values.