Merge "QS: Fix clipping for some font sizes" into nyc-mr1-dev
diff --git a/api/current.txt b/api/current.txt
index 760e3e3..0323098f 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -3705,6 +3705,7 @@
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 99b800bc..aa1d74c 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -3841,6 +3841,7 @@
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 6b9aaaf..d904415 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -3705,6 +3705,7 @@
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 e8fcd3b..e849f4b 100644
--- a/cmds/bootanimation/BootAnimation.cpp
+++ b/cmds/bootanimation/BootAnimation.cpp
@@ -596,15 +596,15 @@
// 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 @@
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 @@
}
}
- 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::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 @@
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 1c3d53a..a093c9b 100644
--- a/cmds/bootanimation/BootAnimation.h
+++ b/cmds/bootanimation/BootAnimation.h
@@ -79,6 +79,10 @@
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 @@
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 0000000..e4c52f7
--- /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 c96bd39e..23fea71 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -3625,6 +3625,24 @@
}
/**
+ * 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 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/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index 9e9dabb..14f9db7 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -2996,6 +2996,13 @@
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 @@
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 4570f1f..d38fb941 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -658,6 +658,8 @@
IIntentReceiver finishedReceiver, String requiredPermission, Bundle options)
throws RemoteException;
+ public void setVrThread(int tid) throws RemoteException;
+
/*
* Private non-Binder interfaces
*/
@@ -1044,4 +1046,5 @@
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 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/os/Process.java b/core/java/android/os/Process.java
index f664e70..b9e46a5 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -322,6 +322,13 @@
*/
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 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/java/android/view/inputmethod/InputContentInfo.java b/core/java/android/view/inputmethod/InputContentInfo.java
index 9579bbf..b39705e 100644
--- a/core/java/android/view/inputmethod/InputContentInfo.java
+++ b/core/java/android/view/inputmethod/InputContentInfo.java
@@ -191,8 +191,6 @@
mUriToken.release();
} catch (RemoteException e) {
e.rethrowFromSystemServer();
- } finally {
- mUriToken = null;
}
}
diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java
index 6477f07..6432f70 100644
--- a/core/java/android/widget/PopupWindow.java
+++ b/core/java/android/widget/PopupWindow.java
@@ -1396,7 +1396,7 @@
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 62e34a6..644c7e9 100644
--- a/core/java/com/android/internal/view/IInputConnectionWrapper.java
+++ b/core/java/com/android/internal/view/IInputConnectionWrapper.java
@@ -562,6 +562,8 @@
}
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 @@
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 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/layout-sw410dp/status_bar_alarm_group.xml b/packages/SystemUI/res/layout-sw410dp/status_bar_alarm_group.xml
index 0b07864..a726161 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">
diff --git a/packages/SystemUI/res/layout/qs_user_detail_item.xml b/packages/SystemUI/res/layout/qs_user_detail_item.xml
index 58fc069..8c6c7cf 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 103e0b0..3b8b909 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 5f87378..701b621 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">
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/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 0365e80..cd861e14 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 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/qs/QSAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
index 2dcb5f4..aaa4e51 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 @@
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 @@
private TouchAnimator mTranslationXAnimator;
private TouchAnimator mTranslationYAnimator;
private TouchAnimator mNonfirstPageAnimator;
- private TouchAnimator mLastRowAnimator;
+ private TouchAnimator mBrightnessAnimator;
private boolean mOnKeyguard;
@@ -144,7 +143,6 @@
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 @@
int[] loc1 = new int[2];
int[] loc2 = new int[2];
int lastXDiff = 0;
- int lastYDiff = 0;
int lastX = 0;
clearAnimationState();
@@ -175,7 +172,6 @@
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 @@
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 @@
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 @@
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 ce6aa71..890279f 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 @@
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 feacaa0..5ed19ef 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 @@
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 40a93df..3861cc3 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 @@
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 @@
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 @@
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 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 82d9782..e5579e2 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 @@
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 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 @@
}
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 @@
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 @@
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 8911a3e..0f7c89d 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 @@
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 @@
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 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/retaildemo/java/com/android/server/retaildemo/RetailDemoModeService.java b/services/retaildemo/java/com/android/server/retaildemo/RetailDemoModeService.java
index d858e82..a4d4013 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 @@
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 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.