diff options
58 files changed, 875 insertions, 159 deletions
diff --git a/api/current.txt b/api/current.txt index 760e3e348184..0323098f303e 100644 --- a/api/current.txt +++ b/api/current.txt @@ -3705,6 +3705,7 @@ package android.app { method public void moveTaskToFront(int, int); method public void moveTaskToFront(int, int, android.os.Bundle); method public deprecated void restartPackage(java.lang.String); + method public static void setVrThread(int); method public void setWatchHeapLimit(long); field public static final java.lang.String ACTION_REPORT_HEAP_LIMIT = "android.app.action.REPORT_HEAP_LIMIT"; field public static final int LOCK_TASK_MODE_LOCKED = 1; // 0x1 diff --git a/api/system-current.txt b/api/system-current.txt index 99b800bca41e..aa1d74cc6ad0 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -3841,6 +3841,7 @@ package android.app { method public void moveTaskToFront(int, int); method public void moveTaskToFront(int, int, android.os.Bundle); method public deprecated void restartPackage(java.lang.String); + method public static void setVrThread(int); method public void setWatchHeapLimit(long); field public static final java.lang.String ACTION_REPORT_HEAP_LIMIT = "android.app.action.REPORT_HEAP_LIMIT"; field public static final int LOCK_TASK_MODE_LOCKED = 1; // 0x1 diff --git a/api/test-current.txt b/api/test-current.txt index 6b9aaaf78b2a..d904415d4e88 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -3705,6 +3705,7 @@ package android.app { method public void moveTaskToFront(int, int); method public void moveTaskToFront(int, int, android.os.Bundle); method public deprecated void restartPackage(java.lang.String); + method public static void setVrThread(int); method public void setWatchHeapLimit(long); field public static final java.lang.String ACTION_REPORT_HEAP_LIMIT = "android.app.action.REPORT_HEAP_LIMIT"; field public static final int LOCK_TASK_MODE_LOCKED = 1; // 0x1 diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp index e8fcd3b8db5d..e849f4b8e9d3 100644 --- a/cmds/bootanimation/BootAnimation.cpp +++ b/cmds/bootanimation/BootAnimation.cpp @@ -596,15 +596,15 @@ bool BootAnimation::preloadZip(Animation& animation) // read all the data structures const size_t pcount = animation.parts.size(); void *cookie = NULL; - ZipFileRO* mZip = animation.zip; - if (!mZip->startIteration(&cookie)) { + ZipFileRO* zip = animation.zip; + if (!zip->startIteration(&cookie)) { return false; } ZipEntryRO entry; char name[ANIM_ENTRY_NAME_MAX]; - while ((entry = mZip->nextEntry(cookie)) != NULL) { - const int foundEntryName = mZip->getEntryFileName(entry, name, ANIM_ENTRY_NAME_MAX); + while ((entry = zip->nextEntry(cookie)) != NULL) { + const int foundEntryName = zip->getEntryFileName(entry, name, ANIM_ENTRY_NAME_MAX); if (foundEntryName > ANIM_ENTRY_NAME_MAX || foundEntryName == -1) { ALOGE("Error fetching entry file name"); continue; @@ -614,22 +614,29 @@ bool BootAnimation::preloadZip(Animation& animation) const String8 path(entryName.getPathDir()); const String8 leaf(entryName.getPathLeaf()); if (leaf.size() > 0) { - for (size_t j=0 ; j<pcount ; j++) { + for (size_t j = 0; j < pcount; j++) { if (path == animation.parts[j].path) { uint16_t method; // supports only stored png files - if (mZip->getEntryInfo(entry, &method, NULL, NULL, NULL, NULL, NULL)) { + if (zip->getEntryInfo(entry, &method, NULL, NULL, NULL, NULL, NULL)) { if (method == ZipFileRO::kCompressStored) { - FileMap* map = mZip->createEntryFileMap(entry); + FileMap* map = zip->createEntryFileMap(entry); if (map) { Animation::Part& part(animation.parts.editItemAt(j)); if (leaf == "audio.wav") { // a part may have at most one audio file part.audioFile = map; + } else if (leaf == "trim.txt") { + part.trimData.setTo((char const*)map->getDataPtr(), + map->getDataLength()); } else { Animation::Frame frame; frame.name = leaf; frame.map = map; + frame.trimWidth = animation.width; + frame.trimHeight = animation.height; + frame.trimX = 0; + frame.trimY = 0; part.frames.add(frame); } } @@ -640,7 +647,33 @@ bool BootAnimation::preloadZip(Animation& animation) } } - mZip->endIteration(cookie); + // If there is trimData present, override the positioning defaults. + for (Animation::Part& part : animation.parts) { + const char* trimDataStr = part.trimData.string(); + for (size_t frameIdx = 0; frameIdx < part.frames.size(); frameIdx++) { + const char* endl = strstr(trimDataStr, "\n"); + // No more trimData for this part. + if (endl == NULL) { + break; + } + String8 line(trimDataStr, endl - trimDataStr); + const char* lineStr = line.string(); + trimDataStr = ++endl; + int width = 0, height = 0, x = 0, y = 0; + if (sscanf(lineStr, "%dx%d+%d+%d", &width, &height, &x, &y) == 4) { + Animation::Frame& frame(part.frames.editItemAt(frameIdx)); + frame.trimWidth = width; + frame.trimHeight = height; + frame.trimX = x; + frame.trimY = y; + } else { + ALOGE("Error parsing trim.txt, line: %s", lineStr); + break; + } + } + } + + zip->endIteration(cookie); return true; } @@ -707,12 +740,9 @@ bool BootAnimation::movie() bool BootAnimation::playAnimation(const Animation& animation) { const size_t pcount = animation.parts.size(); - const int xc = (mWidth - animation.width) / 2; - const int yc = ((mHeight - animation.height) / 2); nsecs_t frameDuration = s2ns(1) / animation.fps; - - Region clearReg(Rect(mWidth, mHeight)); - clearReg.subtractSelf(Rect(xc, yc, xc+animation.width, yc+animation.height)); + const int animationX = (mWidth - animation.width) / 2; + const int animationY = (mHeight - animation.height) / 2; for (size_t i=0 ; i<pcount ; i++) { const Animation::Part& part(animation.parts[i]); @@ -759,22 +789,25 @@ bool BootAnimation::playAnimation(const Animation& animation) initTexture(frame); } + const int xc = animationX + frame.trimX; + const int yc = animationY + frame.trimY; + Region clearReg(Rect(mWidth, mHeight)); + clearReg.subtractSelf(Rect(xc, yc, xc+frame.trimWidth, yc+frame.trimHeight)); if (!clearReg.isEmpty()) { Region::const_iterator head(clearReg.begin()); Region::const_iterator tail(clearReg.end()); glEnable(GL_SCISSOR_TEST); while (head != tail) { const Rect& r2(*head++); - glScissor(r2.left, mHeight - r2.bottom, - r2.width(), r2.height()); + glScissor(r2.left, mHeight - r2.bottom, r2.width(), r2.height()); glClear(GL_COLOR_BUFFER_BIT); } glDisable(GL_SCISSOR_TEST); } - // specify the y center as ceiling((mHeight - animation.height) / 2) - // which is equivalent to mHeight - (yc + animation.height) - glDrawTexiOES(xc, mHeight - (yc + animation.height), - 0, animation.width, animation.height); + // specify the y center as ceiling((mHeight - frame.trimHeight) / 2) + // which is equivalent to mHeight - (yc + frame.trimHeight) + glDrawTexiOES(xc, mHeight - (yc + frame.trimHeight), + 0, frame.trimWidth, frame.trimHeight); if (mClockEnabled && mTimeIsAccurate && part.clockPosY >= 0) { drawTime(mClock, part.clockPosY); } diff --git a/cmds/bootanimation/BootAnimation.h b/cmds/bootanimation/BootAnimation.h index 1c3d53a59bb8..a093c9b87e75 100644 --- a/cmds/bootanimation/BootAnimation.h +++ b/cmds/bootanimation/BootAnimation.h @@ -79,6 +79,10 @@ private: struct Frame { String8 name; FileMap* map; + int trimX; + int trimY; + int trimWidth; + int trimHeight; mutable GLuint tid; bool operator < (const Frame& rhs) const { return name < rhs.name; @@ -90,6 +94,7 @@ private: int clockPosY; // The y position of the clock, in pixels, from the bottom of the // display (the clock is centred horizontally). -1 to disable the clock String8 path; + String8 trimData; SortedVector<Frame> frames; bool playUntilComplete; float backgroundColor[3]; diff --git a/cmds/bootanimation/FORMAT.md b/cmds/bootanimation/FORMAT.md new file mode 100644 index 000000000000..e4c52f783fa3 --- /dev/null +++ b/cmds/bootanimation/FORMAT.md @@ -0,0 +1,127 @@ +# bootanimation format + +## zipfile paths + +The system selects a boot animation zipfile from the following locations, in order: + + /system/media/bootanimation-encrypted.zip (if getprop("vold.decrypt") = '1') + /system/media/bootanimation.zip + /oem/media/bootanimation.zip + +## zipfile layout + +The `bootanimation.zip` archive file includes: + + desc.txt - a text file + part0 \ + part1 \ directories full of PNG frames + ... / + partN / + +## desc.txt format + +The first line defines the general parameters of the animation: + + WIDTH HEIGHT FPS + + * **WIDTH:** animation width (pixels) + * **HEIGHT:** animation height (pixels) + * **FPS:** frames per second, e.g. 60 + +It is followed by a number of rows of the form: + + TYPE COUNT PAUSE PATH [#RGBHEX CLOCK] + + * **TYPE:** a single char indicating what type of animation segment this is: + + `p` -- this part will play unless interrupted by the end of the boot + + `c` -- this part will play to completion, no matter what + * **COUNT:** how many times to play the animation, or 0 to loop forever until boot is complete + * **PAUSE:** number of FRAMES to delay after this part ends + * **PATH:** directory in which to find the frames for this part (e.g. `part0`) + * **RGBHEX:** _(OPTIONAL)_ a background color, specified as `#RRGGBB` + * **CLOCK:** _(OPTIONAL)_ the y-coordinate at which to draw the current time (for watches) + +There is also a special TYPE, `$SYSTEM`, that loads `/system/media/bootanimation.zip` +and plays that. + +## loading and playing frames + +Each part is scanned and loaded directly from the zip archive. Within a part directory, every file +(except `trim.txt` and `audio.wav`; see next sections) is expected to be a PNG file that represents +one frame in that part (at the specified resolution). For this reason it is important that frames be +named sequentially (e.g. `part000.png`, `part001.png`, ...) and added to the zip archive in that +order. + +## trim.txt + +To save on memory, textures may be trimmed by their background color. trim.txt sequentially lists +the trim output for each frame in its directory, so the frames may be properly positioned. +Output should be of the form: `WxH+X+Y`. Example: + + 713x165+388+914 + 708x152+388+912 + 707x139+388+911 + 649x92+388+910 + +If the file is not present, each frame is assumed to be the same size as the animation. + +## audio.wav + +Each part may optionally play a `wav` sample when it starts. To enable this for an animation, +you must also include a `audio_conf.txt` file in the ZIP archive. Its format is as follows: + + card=<ALSA card number> + device=<ALSA device number> + period_size=<period size> + period_count=<period count> + +This header is followed by zero or more mixer settings, each with the format: + + mixer "<name>" = <value list> + +Here's an example `audio_conf.txt` from Shamu: + + card=0 + device=15 + period_size=1024 + period_count=4 + + mixer "QUAT_MI2S_RX Audio Mixer MultiMedia5" = 1 + mixer "Playback Channel Map" = 0 220 157 195 0 0 0 0 + mixer "QUAT_MI2S_RX Channels" = Two + mixer "BOOST_STUB Right Mixer right" = 1 + mixer "BOOST_STUB Left Mixer left" = 1 + mixer "Compress Playback 9 Volume" = 80 80 + +You will probably need to get these mixer names and values out of `audio_platform_info.xml` +and `mixer_paths.xml` for your device. + +## exiting + +The system will end the boot animation (first completing any incomplete or even entirely unplayed +parts that are of type `c`) when the system is finished booting. (This is accomplished by setting +the system property `service.bootanim.exit` to a nonzero string.) + +## protips + +### PNG compression + +Use `zopflipng` if you have it, otherwise `pngcrush` will do. e.g.: + + for fn in *.png ; do + zopflipng -m ${fn}s ${fn}s.new && mv -f ${fn}s.new ${fn} + # or: pngcrush -q .... + done + +Some animations benefit from being reduced to 256 colors: + + pngquant --force --ext .png *.png + # alternatively: mogrify -colors 256 anim-tmp/*/*.png + +### creating the ZIP archive + + cd <path-to-pieces> + zip -0qry -i \*.txt \*.png \*.wav @ ../bootanimation.zip *.txt part* + +Note that the ZIP archive is not actually compressed! The PNG files are already as compressed +as they can reasonably get, and there is unlikely to be any redundancy between files. diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index c96bd39eeffe..23fea7133015 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -3625,6 +3625,24 @@ public class ActivityManager { } /** + * Enable more aggressive scheduling for latency-sensitive low-runtime VR threads. Only one + * thread can be a VR thread in a process at a time, and that thread may be subject to + * restrictions on the amount of time it can run. + * + * To reset the VR thread for an application, a tid of 0 can be passed. + * + * @see android.os.Process#myTid() + * @param tid tid of the VR thread + */ + public static void setVrThread(int tid) { + try { + ActivityManagerNative.getDefault().setVrThread(tid); + } catch (RemoteException e) { + // pass + } + } + + /** * The AppTask allows you to manage your own application's tasks. * See {@link android.app.ActivityManager#getAppTasks()} */ diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java index 33ae553fb57f..6dd14fd263e3 100644 --- a/core/java/android/app/ActivityManagerInternal.java +++ b/core/java/android/app/ActivityManagerInternal.java @@ -19,7 +19,9 @@ package android.app; 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 class ActivityManagerInternal { */ 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/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java index 9e9dabbb6aaf..14f9db732514 100644 --- a/core/java/android/app/ActivityManagerNative.java +++ b/core/java/android/app/ActivityManagerNative.java @@ -2996,6 +2996,13 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM reply.writeInt(result); return true; } + case SET_VR_THREAD_TRANSACTION: { + data.enforceInterface(IActivityManager.descriptor); + final int tid = data.readInt(); + setVrThread(tid); + reply.writeNoException(); + return true; + } } return super.onTransact(code, data, reply, flags); @@ -7031,5 +7038,19 @@ class ActivityManagerProxy implements IActivityManager return res; } + @Override + public void setVrThread(int tid) + throws RemoteException { + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + data.writeInterfaceToken(IActivityManager.descriptor); + data.writeInt(tid); + mRemote.transact(SET_VR_THREAD_TRANSACTION, data, reply, 0); + reply.readException(); + data.recycle(); + reply.recycle(); + return; + } + private IBinder mRemote; } diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java index 4570f1fdb206..d38fb94161f5 100644 --- a/core/java/android/app/IActivityManager.java +++ b/core/java/android/app/IActivityManager.java @@ -658,6 +658,8 @@ public interface IActivityManager extends IInterface { IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) throws RemoteException; + public void setVrThread(int tid) throws RemoteException; + /* * Private non-Binder interfaces */ @@ -1044,4 +1046,5 @@ public interface IActivityManager extends IInterface { int START_CONFIRM_DEVICE_CREDENTIAL_INTENT = IBinder.FIRST_CALL_TRANSACTION + 374; int SEND_IDLE_JOB_TRIGGER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 375; int SEND_INTENT_SENDER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 376; + int SET_VR_THREAD_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 377; } diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index 4104d72ec5ad..fa943f203a6e 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -3261,6 +3261,7 @@ public class Notification implements Parcelable 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 430c7e706b64..c19e6382775a 100644 --- a/core/java/android/content/pm/ILauncherApps.aidl +++ b/core/java/android/content/pm/ILauncherApps.aidl @@ -51,7 +51,7 @@ interface ILauncherApps { 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/os/Process.java b/core/java/android/os/Process.java index f664e70cf7be..b9e46a5c60f1 100644 --- a/core/java/android/os/Process.java +++ b/core/java/android/os/Process.java @@ -322,6 +322,13 @@ public class Process { */ public static final int SCHED_IDLE = 5; + /** + * Reset scheduler choice on fork. + * @hide + */ + public static final int SCHED_RESET_ON_FORK = 0x40000000; + + // Keep in sync with SP_* constants of enum type SchedPolicy // declared in system/core/include/cutils/sched_policy.h, // except THREAD_GROUP_DEFAULT does not correspond to any SP_* value. diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 5a5cedf41e9d..dae243b10762 100755 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -6259,6 +6259,24 @@ public final class Settings { 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 d39e91fd98b2..1e765b62e131 100644 --- a/core/java/android/util/ArraySet.java +++ b/core/java/android/util/ArraySet.java @@ -402,11 +402,14 @@ public final class ArraySet<E> implements Collection<E>, Set<E> { 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/java/android/view/inputmethod/InputContentInfo.java b/core/java/android/view/inputmethod/InputContentInfo.java index 9579bbf32835..b39705e0b1fa 100644 --- a/core/java/android/view/inputmethod/InputContentInfo.java +++ b/core/java/android/view/inputmethod/InputContentInfo.java @@ -191,8 +191,6 @@ public final class InputContentInfo implements Parcelable { mUriToken.release(); } catch (RemoteException e) { e.rethrowFromSystemServer(); - } finally { - mUriToken = null; } } diff --git a/core/java/android/widget/LinearLayout.java b/core/java/android/widget/LinearLayout.java index f75b74bb9a14..38d7cd4acaf6 100644 --- a/core/java/android/widget/LinearLayout.java +++ b/core/java/android/widget/LinearLayout.java @@ -890,7 +890,9 @@ public class LinearLayout extends ViewGroup { remainingWeightSum -= childWeight; final int childHeight; - if (lp.height == 0 && (!mAllowInconsistentMeasurement + if (mUseLargestChild && heightMode != MeasureSpec.EXACTLY) { + childHeight = largestChildHeight; + } else if (lp.height == 0 && (!mAllowInconsistentMeasurement || heightMode == MeasureSpec.EXACTLY)) { // This child needs to be laid out from scratch using // only its share of excess space. @@ -1272,7 +1274,9 @@ public class LinearLayout extends ViewGroup { remainingWeightSum -= childWeight; final int childWidth; - if (lp.width == 0 && (!mAllowInconsistentMeasurement + if (mUseLargestChild && widthMode != MeasureSpec.EXACTLY) { + childWidth = largestChildWidth; + } else if (lp.width == 0 && (!mAllowInconsistentMeasurement || widthMode == MeasureSpec.EXACTLY)) { // This child needs to be laid out from scratch using // only its share of excess space. diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java index 6477f079cb7f..6432f703f2ba 100644 --- a/core/java/android/widget/PopupWindow.java +++ b/core/java/android/widget/PopupWindow.java @@ -1396,7 +1396,7 @@ public class PopupWindow { private int computeGravity() { int gravity = Gravity.START | Gravity.TOP; if (mClipToScreen || mClippingEnabled) { - gravity |= Gravity.DISPLAY_CLIP_VERTICAL | Gravity.DISPLAY_CLIP_HORIZONTAL; + gravity |= Gravity.DISPLAY_CLIP_VERTICAL; } return gravity; } diff --git a/core/java/com/android/internal/view/IInputConnectionWrapper.java b/core/java/com/android/internal/view/IInputConnectionWrapper.java index 62e34a6780fc..644c7e90f8b0 100644 --- a/core/java/com/android/internal/view/IInputConnectionWrapper.java +++ b/core/java/com/android/internal/view/IInputConnectionWrapper.java @@ -562,6 +562,8 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub { } case DO_COMMIT_CONTENT: { final int flags = msg.arg1; + final boolean grantUriPermission = + (flags & InputConnection.INPUT_CONTENT_GRANT_READ_URI_PERMISSION) != 0; SomeArgs args = (SomeArgs) msg.obj; try { InputConnection ic = getInputConnection(); @@ -577,9 +579,17 @@ public abstract class IInputConnectionWrapper extends IInputContext.Stub { args.callback.setCommitContentResult(false, args.seq); return; } - args.callback.setCommitContentResult( - ic.commitContent(inputContentInfo, flags, (Bundle) args.arg2), - args.seq); + if (grantUriPermission) { + inputContentInfo.requestPermission(); + } + final boolean result = + ic.commitContent(inputContentInfo, flags, (Bundle) args.arg2); + // If this request is not handled, then there is no reason to keep the URI + // permission. + if (grantUriPermission && !result) { + inputContentInfo.releasePermission(); + } + args.callback.setCommitContentResult(result, args.seq); } catch (RemoteException e) { Log.w(TAG, "Got RemoteException calling commitContent", e); } diff --git a/core/res/res/values/colors_device_defaults.xml b/core/res/res/values/colors_device_defaults.xml index 5518de2d5c24..f33eb6fd2816 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 2ac4092a5079..8a6c229bcdd0 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 1da59c984c52..7d8d8114f000 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 96d7394e5515..d426d1aef072 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 3d6f058c3b37..af13043e22e6 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 c34f474762f0..4f6368c69975 100644 --- a/graphics/java/android/graphics/drawable/VectorDrawable.java +++ b/graphics/java/android/graphics/drawable/VectorDrawable.java @@ -243,9 +243,7 @@ public class VectorDrawable extends Drawable { * 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 @@ public class VectorDrawable extends Drawable { 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 af6aee759eec..5e3bbbb86486 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.pm.PackageManager; 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 @@ final class QuickViewIntentBuilder { @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 @@ final class QuickViewIntentBuilder { 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/EasterEgg/src/com/android/egg/neko/Cat.java b/packages/EasterEgg/src/com/android/egg/neko/Cat.java index 864b20c73fbf..8c44fd6b1767 100644 --- a/packages/EasterEgg/src/com/android/egg/neko/Cat.java +++ b/packages/EasterEgg/src/com/android/egg/neko/Cat.java @@ -28,6 +28,7 @@ import java.util.Random; import java.util.concurrent.ThreadLocalRandom; import com.android.egg.R; +import com.android.internal.logging.MetricsLogger; public class Cat extends Drawable { public static final long[] PURR = {0, 40, 20, 40, 20, 40, 20, 40, 20, 40, 20, 40}; @@ -37,6 +38,8 @@ public class Cat extends Drawable { private long mSeed; private String mName; private int mBodyColor; + private int mFootType; + private boolean mBowTie; private synchronized Random notSoRandom(long seed) { if (mNotSoRandom == null) { @@ -66,6 +69,15 @@ public class Cat extends Drawable { return a[i+1]; } + public static final int getColorIndex(int q, int[] a) { + for(int i = 1; i < a.length; i+=2) { + if (a[i] == q) { + return i/2; + } + } + return -1; + } + public static final int[] P_BODY_COLORS = { 180, 0xFF212121, // black 180, 0xFFFFFFFF, // white @@ -155,14 +167,19 @@ public class Cat extends Drawable { tint(0xFF000000, D.mouth, D.nose); } + mFootType = 0; if (nsr.nextFloat() < 0.25f) { + mFootType = 4; tint(0xFFFFFFFF, D.foot1, D.foot2, D.foot3, D.foot4); } else { if (nsr.nextFloat() < 0.25f) { + mFootType = 2; tint(0xFFFFFFFF, D.foot1, D.foot2); } else if (nsr.nextFloat() < 0.25f) { + mFootType = 3; // maybe -2 would be better? meh. tint(0xFFFFFFFF, D.foot3, D.foot4); } else if (nsr.nextFloat() < 0.1f) { + mFootType = 1; tint(0xFFFFFFFF, (Drawable) choose(nsr, D.foot1, D.foot2, D.foot3, D.foot4)); } } @@ -175,7 +192,8 @@ public class Cat extends Drawable { final int collarColor = chooseP(nsr, P_COLLAR_COLORS); tint(collarColor, D.collar); - tint((nsr.nextFloat() < 0.1f) ? collarColor : 0, D.bowtie); + mBowTie = nsr.nextFloat() < 0.1f; + tint(mBowTie ? collarColor : 0, D.bowtie); } public static Cat create(Context context) { @@ -290,6 +308,26 @@ public class Cat extends Drawable { return mBodyColor; } + public void logAdd(Context context) { + logCatAction(context, "egg_neko_add"); + } + + public void logRemove(Context context) { + logCatAction(context, "egg_neko_remove"); + } + + public void logShare(Context context) { + logCatAction(context, "egg_neko_share"); + } + + private void logCatAction(Context context, String prefix) { + MetricsLogger.count(context, prefix, 1); + MetricsLogger.histogram(context, prefix +"_color", + getColorIndex(mBodyColor, P_BODY_COLORS)); + MetricsLogger.histogram(context, prefix + "_bowtie", mBowTie ? 1 : 0); + MetricsLogger.histogram(context, prefix + "_feet", mFootType); + } + public static class CatParts { public Drawable leftEar; public Drawable rightEar; diff --git a/packages/EasterEgg/src/com/android/egg/neko/NekoActivationActivity.java b/packages/EasterEgg/src/com/android/egg/neko/NekoActivationActivity.java index 88a7968da16c..c0b725c05899 100644 --- a/packages/EasterEgg/src/com/android/egg/neko/NekoActivationActivity.java +++ b/packages/EasterEgg/src/com/android/egg/neko/NekoActivationActivity.java @@ -20,6 +20,8 @@ import android.content.pm.PackageManager; import android.util.Log; import android.widget.Toast; +import com.android.internal.logging.MetricsLogger; + public class NekoActivationActivity extends Activity { private void toastUp(String s) { Toast toast = Toast.makeText(this, s, Toast.LENGTH_SHORT); @@ -39,6 +41,7 @@ public class NekoActivationActivity extends Activity { } pm.setComponentEnabledSetting(cn, PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP); + MetricsLogger.histogram(this, "egg_neko_enable", 0); toastUp("\uD83D\uDEAB"); } else { if (NekoLand.DEBUG) { @@ -46,6 +49,7 @@ public class NekoActivationActivity extends Activity { } pm.setComponentEnabledSetting(cn, PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP); + MetricsLogger.histogram(this, "egg_neko_enable", 1); toastUp("\uD83D\uDC31"); } finish(); diff --git a/packages/EasterEgg/src/com/android/egg/neko/NekoDialog.java b/packages/EasterEgg/src/com/android/egg/neko/NekoDialog.java index a2ffd3e21887..2d2fbe8207af 100644 --- a/packages/EasterEgg/src/com/android/egg/neko/NekoDialog.java +++ b/packages/EasterEgg/src/com/android/egg/neko/NekoDialog.java @@ -26,6 +26,7 @@ import android.widget.ImageView; import android.widget.TextView; import com.android.egg.R; +import com.android.internal.logging.MetricsLogger; import java.util.ArrayList; @@ -51,6 +52,7 @@ public class NekoDialog extends Dialog { if (currentState == 0 && food.getType() != 0) { NekoService.registerJob(getContext(), food.getInterval(getContext())); } + MetricsLogger.histogram(getContext(), "egg_neko_offered_food", food.getType()); prefs.setFoodState(food.getType()); dismiss(); } diff --git a/packages/EasterEgg/src/com/android/egg/neko/NekoLand.java b/packages/EasterEgg/src/com/android/egg/neko/NekoLand.java index e6a41774f3cb..feada7f9dc37 100644 --- a/packages/EasterEgg/src/com/android/egg/neko/NekoLand.java +++ b/packages/EasterEgg/src/com/android/egg/neko/NekoLand.java @@ -44,6 +44,7 @@ import android.widget.TextView; import com.android.egg.R; import com.android.egg.neko.PrefState.PrefsListener; +import com.android.internal.logging.MetricsLogger; import java.io.File; import java.io.FileOutputStream; @@ -79,7 +80,8 @@ public class NekoLand extends Activity implements PrefsListener { mAdapter = new CatAdapter(); recyclerView.setAdapter(mAdapter); recyclerView.setLayoutManager(new GridLayoutManager(this, 3)); - updateCats(); + int numCats = updateCats(); + MetricsLogger.histogram(this, "egg_neko_visit_gallery", numCats); } @Override @@ -88,7 +90,7 @@ public class NekoLand extends Activity implements PrefsListener { mPrefs.setListener(null); } - private void updateCats() { + private int updateCats() { Cat[] cats; if (CAT_GEN) { cats = new Cat[50]; @@ -99,6 +101,7 @@ public class NekoLand extends Activity implements PrefsListener { cats = mPrefs.getCats().toArray(new Cat[0]); } mAdapter.setCats(cats); + return cats.length; } private void onCatClick(Cat cat) { @@ -115,11 +118,12 @@ public class NekoLand extends Activity implements PrefsListener { } private void onCatRemove(Cat cat) { + cat.logRemove(this); mPrefs.removeCat(cat); } private void showNameDialog(final Cat cat) { - Context context = new ContextThemeWrapper(this, + final Context context = new ContextThemeWrapper(this, android.R.style.Theme_Material_Light_Dialog_NoActionBar); // TODO: Move to XML, add correct margins. View view = LayoutInflater.from(context).inflate(R.layout.edit_text, null); @@ -134,6 +138,7 @@ public class NekoLand extends Activity implements PrefsListener { .setPositiveButton(android.R.string.ok, new OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { + MetricsLogger.count(context, "egg_neko_rename_cat", 1); cat.setName(text.getText().toString().trim()); mPrefs.addCat(cat); } @@ -244,6 +249,7 @@ public class NekoLand extends Activity implements PrefsListener { intent.putExtra(Intent.EXTRA_SUBJECT, cat.getName()); intent.setType("image/png"); startActivity(Intent.createChooser(intent, null)); + cat.logShare(this); } catch (IOException e) { Log.e("NekoLand", "save: error: " + e); } diff --git a/packages/EasterEgg/src/com/android/egg/neko/NekoService.java b/packages/EasterEgg/src/com/android/egg/neko/NekoService.java index 1ee385136f47..32e335843016 100644 --- a/packages/EasterEgg/src/com/android/egg/neko/NekoService.java +++ b/packages/EasterEgg/src/com/android/egg/neko/NekoService.java @@ -82,6 +82,7 @@ public class NekoService extends JobService { if (cats.size() == 0 || rng.nextFloat() <= new_cat_prob) { cat = Cat.create(this); prefs.addCat(cat); + cat.logAdd(this); Log.v(TAG, "A new cat is here: " + cat.getName()); } else { cat = cats.get(rng.nextInt(cats.size())); diff --git a/packages/EasterEgg/src/com/android/egg/neko/NekoTile.java b/packages/EasterEgg/src/com/android/egg/neko/NekoTile.java index d5e143cade0f..8a3ec8fa19e9 100644 --- a/packages/EasterEgg/src/com/android/egg/neko/NekoTile.java +++ b/packages/EasterEgg/src/com/android/egg/neko/NekoTile.java @@ -20,6 +20,7 @@ import android.service.quicksettings.TileService; import android.util.Log; import com.android.egg.neko.PrefState.PrefsListener; +import com.android.internal.logging.MetricsLogger; public class NekoTile extends TileService implements PrefsListener { @@ -47,6 +48,18 @@ public class NekoTile extends TileService implements PrefsListener { } @Override + public void onTileAdded() { + super.onTileAdded(); + MetricsLogger.count(this, "egg_neko_tile_added", 1); + } + + @Override + public void onTileRemoved() { + super.onTileRemoved(); + MetricsLogger.count(this, "egg_neko_tile_removed", 1); + } + + @Override public void onPrefsChanged() { updateState(); } @@ -65,6 +78,7 @@ public class NekoTile extends TileService implements PrefsListener { public void onClick() { if (mPrefs.getFoodState() != 0) { // there's already food loaded, let's empty it + MetricsLogger.count(this, "egg_neko_empty_food", 1); mPrefs.setFoodState(0); NekoService.cancelJob(this); } else { @@ -91,6 +105,7 @@ public class NekoTile extends TileService implements PrefsListener { private void showNekoDialog() { Log.d(TAG, "showNekoDialog"); + MetricsLogger.count(this, "egg_neko_select_food", 1); showDialog(new NekoDialog(this)); } } 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 000000000000..4be39c7ca6e0 --- /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/layout-sw410dp/status_bar_alarm_group.xml b/packages/SystemUI/res/layout-sw410dp/status_bar_alarm_group.xml index ba5c0aa0c772..a726161c2c66 100644 --- a/packages/SystemUI/res/layout-sw410dp/status_bar_alarm_group.xml +++ b/packages/SystemUI/res/layout-sw410dp/status_bar_alarm_group.xml @@ -20,7 +20,7 @@ android:id="@+id/date_time_alarm_group" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_marginTop="8dp" + android:layout_marginTop="16dp" android:layout_marginStart="16dp" android:gravity="start" android:orientation="vertical"> @@ -62,7 +62,8 @@ <com.android.systemui.statusbar.AlphaOptimizedButton android:id="@+id/alarm_status" android:layout_width="wrap_content" - android:layout_height="20dp" + android:layout_height="wrap_content" + android:minHeight="20dp" android:paddingTop="3dp" android:drawablePadding="8dp" android:drawableStart="@drawable/ic_access_alarms_small" diff --git a/packages/SystemUI/res/layout/qs_user_detail_item.xml b/packages/SystemUI/res/layout/qs_user_detail_item.xml index 58fc0694c72c..8c6c7cfc8048 100644 --- a/packages/SystemUI/res/layout/qs_user_detail_item.xml +++ b/packages/SystemUI/res/layout/qs_user_detail_item.xml @@ -28,6 +28,7 @@ android:minHeight="112dp" android:clipChildren="false" android:clipToPadding="false" + android:focusable="true" android:background="@drawable/ripple_drawable" systemui:activatedFontFamily="sans-serif-medium"> @@ -65,4 +66,4 @@ android:visibility="gone" /> </LinearLayout> -</com.android.systemui.qs.tiles.UserDetailItemView>
\ No newline at end of file +</com.android.systemui.qs.tiles.UserDetailItemView> diff --git a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml index 103e0b04e6e8..3b8b909118f1 100644 --- a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml +++ b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml @@ -44,6 +44,7 @@ android:layout_width="48dp" android:layout_height="48dp" android:layout_alignParentEnd="true" + android:focusable="true" android:background="@drawable/ripple_drawable" > <ImageView android:id="@+id/multi_user_avatar" android:layout_width="@dimen/multi_user_avatar_expanded_size" @@ -121,7 +122,6 @@ <include android:id="@+id/date_time_alarm_group" layout="@layout/status_bar_alarm_group" - android:layout_marginTop="16dp" android:layout_alignParentStart="true" android:layout_alignParentTop="true" /> diff --git a/packages/SystemUI/res/layout/status_bar_alarm_group.xml b/packages/SystemUI/res/layout/status_bar_alarm_group.xml index f94b72794c97..701b621c469b 100644 --- a/packages/SystemUI/res/layout/status_bar_alarm_group.xml +++ b/packages/SystemUI/res/layout/status_bar_alarm_group.xml @@ -20,7 +20,7 @@ android:id="@+id/date_time_alarm_group" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_marginTop="8dp" + android:layout_marginTop="16dp" android:layout_marginStart="16dp" android:gravity="start" android:orientation="vertical"> @@ -60,7 +60,8 @@ <com.android.systemui.statusbar.AlphaOptimizedButton android:id="@+id/alarm_status" android:layout_width="wrap_content" - android:layout_height="20dp" + android:layout_height="wrap_content" + android:minHeight="20dp" android:paddingTop="3dp" android:drawablePadding="8dp" android:drawableStart="@drawable/ic_access_alarms_small" diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml index d12ef42590dc..9061376a3279 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/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 0365e800f34a..cd861e14b18b 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -172,8 +172,6 @@ <dimen name="qs_tile_margin_top">16dp</dimen> <dimen name="qs_quick_tile_size">48dp</dimen> <dimen name="qs_quick_tile_padding">12dp</dimen> - <dimen name="qs_date_collapsed_text_size">14sp</dimen> - <dimen name="qs_date_text_size">16sp</dimen> <dimen name="qs_header_gear_translation">16dp</dimen> <dimen name="qs_page_indicator_width">16dp</dimen> <dimen name="qs_page_indicator_height">8dp</dimen> diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml index e5681ce517f9..1ee13e96ecfe 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/qs/QSAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java index 2dcb5f492688..aaa4e51ae832 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java @@ -41,8 +41,7 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha private static final String ALLOW_FANCY_ANIMATION = "sysui_qs_fancy_anim"; private static final String MOVE_FULL_ROWS = "sysui_qs_move_whole_rows"; - public static final float EXPANDED_TILE_DELAY = .7f; - private static final float LAST_ROW_EXPANDED_DELAY = .86f; + public static final float EXPANDED_TILE_DELAY = .86f; private final ArrayList<View> mAllViews = new ArrayList<>(); private final ArrayList<View> mTopFiveQs = new ArrayList<>(); @@ -58,7 +57,7 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha private TouchAnimator mTranslationXAnimator; private TouchAnimator mTranslationYAnimator; private TouchAnimator mNonfirstPageAnimator; - private TouchAnimator mLastRowAnimator; + private TouchAnimator mBrightnessAnimator; private boolean mOnKeyguard; @@ -144,7 +143,6 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha TouchAnimator.Builder firstPageBuilder = new Builder(); TouchAnimator.Builder translationXBuilder = new Builder(); TouchAnimator.Builder translationYBuilder = new Builder(); - TouchAnimator.Builder lastRowBuilder = new Builder(); if (mQsPanel.getHost() == null) return; Collection<QSTile<?>> tiles = mQsPanel.getHost().getTiles(); @@ -152,7 +150,6 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha int[] loc1 = new int[2]; int[] loc2 = new int[2]; int lastXDiff = 0; - int lastYDiff = 0; int lastX = 0; clearAnimationState(); @@ -175,7 +172,6 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha final int xDiff = loc2[0] - loc1[0]; final int yDiff = loc2[1] - loc1[1]; lastXDiff = loc1[0] - lastX; - lastYDiff = yDiff; // Move the quick tile right from its location to the new one. translationXBuilder.addFloat(quickTileView, "translationX", 0, xDiff); translationYBuilder.addFloat(quickTileView, "translationY", 0, yDiff); @@ -209,13 +205,25 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha mAllViews.add(tileIcon); } else { - lastRowBuilder.addFloat(tileView, "alpha", 0, 1); + firstPageBuilder.addFloat(tileView, "alpha", 0, 1); } mAllViews.add(tileView); mAllViews.add(label); count++; } if (mAllowFancy) { + // Make brightness appear static position and alpha in through second half. + View brightness = mQsPanel.getBrightnessView(); + if (brightness != null) { + firstPageBuilder.addFloat(brightness, "translationY", mQsPanel.getHeight(), 0); + mBrightnessAnimator = new TouchAnimator.Builder() + .addFloat(brightness, "alpha", 0, 1) + .setStartDelay(.5f) + .build(); + mAllViews.add(brightness); + } else { + mBrightnessAnimator = null; + } mFirstPageAnimator = firstPageBuilder .setListener(this) .build(); @@ -223,9 +231,6 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha mFirstPageDelayedAnimator = new TouchAnimator.Builder() .setStartDelay(EXPANDED_TILE_DELAY) .addFloat(mQsPanel.getTileLayout(), "alpha", 0, 1).build(); - mLastRowAnimator = lastRowBuilder - .setStartDelay(LAST_ROW_EXPANDED_DELAY) - .build(); Path path = new Path(); path.moveTo(0, 0); path.cubicTo(0, 0, 0, 1, 1, 1); @@ -279,7 +284,9 @@ public class QSAnimator implements Callback, PageListener, Listener, OnLayoutCha mFirstPageDelayedAnimator.setPosition(position); mTranslationXAnimator.setPosition(position); mTranslationYAnimator.setPosition(position); - mLastRowAnimator.setPosition(position); + if (mBrightnessAnimator != null) { + mBrightnessAnimator.setPosition(position); + } } else { mNonfirstPageAnimator.setPosition(position); } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java index ce6aa7183356..890279f22a5f 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanel.java @@ -166,6 +166,10 @@ public class QSPanel extends LinearLayout implements Tunable, Callback { brightnessSlider.setMirrorController(c); } + View getBrightnessView() { + return mBrightnessView; + } + public void setCallback(Callback callback) { mCallback = callback; } diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileBaseView.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileBaseView.java index feacaa02550b..5ed19efa4b44 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/QSTileBaseView.java +++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileBaseView.java @@ -63,6 +63,7 @@ public class QSTileBaseView extends LinearLayout { setClipChildren(false); setClipToPadding(false); mCollapsedView = collapsedView; + setFocusable(true); } private Drawable newTileBackground() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStatusBarHeader.java index 40a93df47002..3861cc30392b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStatusBarHeader.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStatusBarHeader.java @@ -83,12 +83,9 @@ public class QuickStatusBarHeader extends BaseStatusBarHeader implements protected MultiUserSwitch mMultiUserSwitch; private ImageView mMultiUserAvatar; - private float mDateScaleFactor; - protected float mGearTranslation; private TouchAnimator mSecondHalfAnimator; private TouchAnimator mFirstHalfAnimator; - private TouchAnimator mDateSizeAnimator; protected TouchAnimator mSettingsAlpha; private float mExpansionAmount; private QSTileHost mHost; @@ -155,41 +152,23 @@ public class QuickStatusBarHeader extends BaseStatusBarHeader implements FontSizeUtils.updateFontSize(mAlarmStatus, R.dimen.qs_date_collapsed_size); FontSizeUtils.updateFontSize(mEmergencyOnly, R.dimen.qs_emergency_calls_only_text_size); - mGearTranslation = mContext.getResources().getDimension(R.dimen.qs_header_gear_translation); - - float dateCollapsedSize = mContext.getResources().getDimension( - R.dimen.qs_date_collapsed_text_size); - float dateExpandedSize = mContext.getResources().getDimension( - R.dimen.qs_date_text_size); - mDateScaleFactor = dateExpandedSize / dateCollapsedSize; - mSecondHalfAnimator = new TouchAnimator.Builder() .addFloat(mShowFullAlarm ? mAlarmStatus : findViewById(R.id.date), "alpha", 0, 1) .addFloat(mEmergencyOnly, "alpha", 0, 1) - .setStartDelay(.5f) .build(); if (mShowFullAlarm) { mFirstHalfAnimator = new TouchAnimator.Builder() .addFloat(mAlarmStatusCollapsed, "alpha", 1, 0) - .setEndDelay(.5f) .build(); } - mDateSizeAnimator = new TouchAnimator.Builder() - .addFloat(mDateTimeGroup, "scaleX", 1, mDateScaleFactor) - .addFloat(mDateTimeGroup, "scaleY", 1, mDateScaleFactor) - .setStartDelay(.36f) - .build(); updateSettingsAnimator(); } protected void updateSettingsAnimator() { mSettingsAlpha = new TouchAnimator.Builder() - .addFloat(mEdit, "translationY", -mGearTranslation, 0) - .addFloat(mMultiUserSwitch, "translationY", -mGearTranslation, 0) .addFloat(mEdit, "alpha", 0, 1) .addFloat(mMultiUserSwitch, "alpha", 0, 1) - .setStartDelay(QSAnimator.EXPANDED_TILE_DELAY) .build(); final boolean isRtl = isLayoutRtl(); @@ -248,7 +227,6 @@ public class QuickStatusBarHeader extends BaseStatusBarHeader implements if (mShowFullAlarm) { mFirstHalfAnimator.setPosition(headerExpansionFraction); } - mDateSizeAnimator.setPosition(headerExpansionFraction); mSettingsAlpha.setPosition(headerExpansionFraction); updateAlarmVisibilities(); 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 379ad53c6dba..27ba003c6ef1 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 @@ public class UserSwitcherController { } 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 8881c7939337..6e08139267ef 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 @@ public class BatteryPreference extends DropDownPreference implements TunerServic @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 ea92443b7265..caa0527625d9 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 @@ public class ClockPreference extends DropDownPreference implements TunerService. 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 @@ public class ClockPreference extends DropDownPreference implements TunerService. @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 e0d89f2fe6d3..71ac54492cb6 100644 --- a/services/core/java/com/android/server/InputMethodManagerService.java +++ b/services/core/java/com/android/server/InputMethodManagerService.java @@ -177,6 +177,8 @@ public class InputMethodManagerService extends IInputMethodManager.Stub 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 @@ public class InputMethodManagerService extends IInputMethodManager.Stub @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 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } @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 @@ public class InputMethodManagerService extends IInputMethodManager.Stub 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 d48aeed6af07..e63f5365bff8 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 @@ import java.io.PrintWriter; /** * <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 @@ public final class PinnerService extends SystemService { 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 82d9782c91d6..e5579e29c208 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -2310,6 +2310,20 @@ public final class ActivityManagerService extends ActivityManagerNative if (mInVrMode != vrMode) { mInVrMode = vrMode; mShowDialogs = shouldShowDialogs(mConfiguration, mInVrMode); + if (r.app != null) { + ProcessRecord proc = r.app; + if (proc.vrThreadTid > 0) { + if (proc.curSchedGroup == ProcessList.SCHED_GROUP_TOP_APP) { + if (mInVrMode == true) { + Process.setThreadScheduler(proc.vrThreadTid, + Process.SCHED_FIFO | Process.SCHED_RESET_ON_FORK, 1); + } else { + Process.setThreadScheduler(proc.vrThreadTid, + Process.SCHED_OTHER, 0); + } + } + } + } } } vrService.setVrMode(vrMode, requestedPackage, userId, callingPackage); @@ -12508,6 +12522,34 @@ public final class ActivityManagerService extends ActivityManagerNative } } + public void setVrThread(int tid) { + if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_VR_MODE)) { + throw new UnsupportedOperationException("VR mode not supported on this device!"); + } + + synchronized (this) { + ProcessRecord proc; + synchronized (mPidsSelfLocked) { + final int pid = Binder.getCallingPid(); + proc = mPidsSelfLocked.get(pid); + if (proc != null && mInVrMode && tid >= 0) { + // reset existing VR thread to CFS + if (proc.vrThreadTid != 0) { + Process.setThreadScheduler(proc.vrThreadTid, Process.SCHED_OTHER, 0); + } + // add check to guarantee that tid belongs to pid? + proc.vrThreadTid = tid; + // promote to FIFO now if the tid is non-zero + if (proc.curSchedGroup == ProcessList.SCHED_GROUP_TOP_APP && proc.vrThreadTid > 0) { + Process.setThreadScheduler(proc.vrThreadTid, Process.SCHED_FIFO | Process.SCHED_RESET_ON_FORK, 1); + } + } else { + //Slog.e("VR_FIFO", "Didn't set thread from setVrThread?"); + } + } + } + } + @Override public int setVrMode(IBinder token, boolean enabled, ComponentName packageName) { if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_VR_MODE)) { @@ -20107,6 +20149,7 @@ public final class ActivityManagerService extends ActivityManagerNative } if (app.setSchedGroup != app.curSchedGroup) { + int oldSchedGroup = app.setSchedGroup; app.setSchedGroup = app.curSchedGroup; if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Slog.v(TAG_OOM_ADJ, "Setting sched group of " + app.processName @@ -20132,6 +20175,23 @@ public final class ActivityManagerService extends ActivityManagerNative long oldId = Binder.clearCallingIdentity(); try { Process.setProcessGroup(app.pid, processGroup); + if (app.curSchedGroup == ProcessList.SCHED_GROUP_TOP_APP) { + // do nothing if we already switched to RT + if (oldSchedGroup != ProcessList.SCHED_GROUP_TOP_APP) { + // Switch VR thread for app to SCHED_FIFO + if (mInVrMode && app.vrThreadTid != 0) { + Process.setThreadScheduler(app.vrThreadTid, + Process.SCHED_FIFO | Process.SCHED_RESET_ON_FORK, 1); + } + } + } else if (oldSchedGroup == ProcessList.SCHED_GROUP_TOP_APP && + app.curSchedGroup != ProcessList.SCHED_GROUP_TOP_APP) { + // Reset VR thread to SCHED_OTHER + // Safe to do even if we're not in VR mode + if (app.vrThreadTid != 0) { + Process.setThreadScheduler(app.vrThreadTid, Process.SCHED_OTHER, 0); + } + } } catch (Exception e) { Slog.w(TAG, "Failed setting process group of " + app.pid + " to " + app.curSchedGroup); @@ -21664,6 +21724,33 @@ public final class ActivityManagerService extends ActivityManagerNative 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/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java index 8911a3e94979..0f7c89dc9506 100644 --- a/services/core/java/com/android/server/am/ProcessRecord.java +++ b/services/core/java/com/android/server/am/ProcessRecord.java @@ -97,6 +97,7 @@ final class ProcessRecord { int verifiedAdj; // The last adjustment that was verified as actually being set int curSchedGroup; // Currently desired scheduling class int setSchedGroup; // Last set to background scheduling class + int vrThreadTid; // Thread currently set for VR scheduling int trimMemoryLevel; // Last selected memory trimming level int curProcState = PROCESS_STATE_NONEXISTENT; // Currently computed process state int repProcState = PROCESS_STATE_NONEXISTENT; // Last reported process state @@ -293,6 +294,7 @@ final class ProcessRecord { pw.print(" setSchedGroup="); pw.print(setSchedGroup); pw.print(" systemNoUi="); pw.print(systemNoUi); pw.print(" trimMemoryLevel="); pw.println(trimMemoryLevel); + pw.print(prefix); pw.print("vrThreadTid="); pw.print(vrThreadTid); pw.print(prefix); pw.print("curProcState="); pw.print(curProcState); pw.print(" repProcState="); pw.print(repProcState); pw.print(" pssProcState="); pw.print(pssProcState); diff --git a/services/core/java/com/android/server/pm/LauncherAppsService.java b/services/core/java/com/android/server/pm/LauncherAppsService.java index 46da60764e7e..03d5645f7e5d 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 @@ package com.android.server.pm; 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 @@ public class LauncherAppsService extends SystemService { 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 @@ public class LauncherAppsService extends SystemService { 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 @@ public class LauncherAppsService extends SystemService { } @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 @@ public class LauncherAppsService extends SystemService { 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 114ad968d6dc..2ebb9f6ace58 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 @@ public class PackageManagerService extends IPackageManager.Stub { 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 007b73814256..8f9968ecf7fd 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 @@ public final class PermissionsState { 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 @@ public final class PermissionsState { 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 4d91814af44a..2e5eb3ac83f5 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.content.pm.ResolveInfo; 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 android.util.SparseArray; 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 @@ import java.util.List; */ 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 @@ public class SearchManagerService extends ISearchManager.Stub { } @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 @@ public class SearchManagerService extends ISearchManager.Stub { 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 7c2ba1eaaac1..79fe18b77585 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 @@ public class WallpaperManagerService extends IWallpaperManager.Stub { 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 @@ public class WallpaperManagerService extends IWallpaperManager.Stub { 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/retaildemo/java/com/android/server/retaildemo/RetailDemoModeService.java b/services/retaildemo/java/com/android/server/retaildemo/RetailDemoModeService.java index d858e822cdbe..a4d401318793 100644 --- a/services/retaildemo/java/com/android/server/retaildemo/RetailDemoModeService.java +++ b/services/retaildemo/java/com/android/server/retaildemo/RetailDemoModeService.java @@ -247,6 +247,7 @@ public class RetailDemoModeService extends SystemService { um.setUserRestriction(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES, true, user); um.setUserRestriction(UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS, true, user); um.setUserRestriction(UserManager.DISALLOW_USB_FILE_TRANSFER, true, user); + um.setUserRestriction(UserManager.DISALLOW_MODIFY_ACCOUNTS, true, user); Settings.Secure.putIntForUser(getContext().getContentResolver(), Settings.Secure.SKIP_FIRST_USE_HINTS, 1, userInfo.id); } 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 7cf03af23d05..b6084d5a1387 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.Nullable; 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 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { 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 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { 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 @@ public abstract class BaseShortcutManagerTest extends InstrumentationTestCase { 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. |