diff options
30 files changed, 620 insertions, 101 deletions
diff --git a/api/current.txt b/api/current.txt index f7b15ba9ed0f..53791405683c 100644 --- a/api/current.txt +++ b/api/current.txt @@ -36771,6 +36771,7 @@ package android.telephony { field public static final java.lang.String KEY_ALLOW_EMERGENCY_NUMBERS_IN_CALL_LOG_BOOL = "allow_emergency_numbers_in_call_log_bool"; field public static final java.lang.String KEY_ALLOW_EMERGENCY_VIDEO_CALLS_BOOL = "allow_emergency_video_calls_bool"; field public static final java.lang.String KEY_ALLOW_LOCAL_DTMF_TONES_BOOL = "allow_local_dtmf_tones_bool"; + field public static final java.lang.String KEY_ALLOW_MERGE_WIFI_CALLS_WHEN_VOWIFI_OFF_BOOL = "allow_merge_wifi_calls_when_vowifi_off_bool"; field public static final java.lang.String KEY_ALLOW_NON_EMERGENCY_CALLS_IN_ECM_BOOL = "allow_non_emergency_calls_in_ecm_bool"; field public static final java.lang.String KEY_ALWAYS_SHOW_EMERGENCY_ALERT_ONOFF_BOOL = "always_show_emergency_alert_onoff_bool"; field public static final java.lang.String KEY_APN_EXPAND_BOOL = "apn_expand_bool"; diff --git a/api/system-current.txt b/api/system-current.txt index bf1842e85b0d..6a6653357d10 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -39810,6 +39810,7 @@ package android.telephony { field public static final java.lang.String KEY_ALLOW_EMERGENCY_NUMBERS_IN_CALL_LOG_BOOL = "allow_emergency_numbers_in_call_log_bool"; field public static final java.lang.String KEY_ALLOW_EMERGENCY_VIDEO_CALLS_BOOL = "allow_emergency_video_calls_bool"; field public static final java.lang.String KEY_ALLOW_LOCAL_DTMF_TONES_BOOL = "allow_local_dtmf_tones_bool"; + field public static final java.lang.String KEY_ALLOW_MERGE_WIFI_CALLS_WHEN_VOWIFI_OFF_BOOL = "allow_merge_wifi_calls_when_vowifi_off_bool"; field public static final java.lang.String KEY_ALLOW_NON_EMERGENCY_CALLS_IN_ECM_BOOL = "allow_non_emergency_calls_in_ecm_bool"; field public static final java.lang.String KEY_ALWAYS_SHOW_EMERGENCY_ALERT_ONOFF_BOOL = "always_show_emergency_alert_onoff_bool"; field public static final java.lang.String KEY_APN_EXPAND_BOOL = "apn_expand_bool"; diff --git a/api/test-current.txt b/api/test-current.txt index fa066224a47a..7642282f4aca 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -36849,6 +36849,7 @@ package android.telephony { field public static final java.lang.String KEY_ALLOW_EMERGENCY_NUMBERS_IN_CALL_LOG_BOOL = "allow_emergency_numbers_in_call_log_bool"; field public static final java.lang.String KEY_ALLOW_EMERGENCY_VIDEO_CALLS_BOOL = "allow_emergency_video_calls_bool"; field public static final java.lang.String KEY_ALLOW_LOCAL_DTMF_TONES_BOOL = "allow_local_dtmf_tones_bool"; + field public static final java.lang.String KEY_ALLOW_MERGE_WIFI_CALLS_WHEN_VOWIFI_OFF_BOOL = "allow_merge_wifi_calls_when_vowifi_off_bool"; field public static final java.lang.String KEY_ALLOW_NON_EMERGENCY_CALLS_IN_ECM_BOOL = "allow_non_emergency_calls_in_ecm_bool"; field public static final java.lang.String KEY_ALWAYS_SHOW_EMERGENCY_ALERT_ONOFF_BOOL = "always_show_emergency_alert_onoff_bool"; field public static final java.lang.String KEY_APN_EXPAND_BOOL = "apn_expand_bool"; diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java index c6834f940554..32a8088e9c4e 100644 --- a/cmds/pm/src/com/android/commands/pm/Pm.java +++ b/cmds/pm/src/com/android/commands/pm/Pm.java @@ -922,6 +922,8 @@ public final class Pm { flags |= UserInfo.FLAG_EPHEMERAL; } else if ("--guest".equals(opt)) { flags |= UserInfo.FLAG_GUEST; + } else if ("--demo".equals(opt)) { + flags |= UserInfo.FLAG_DEMO; } else { System.err.println("Error: unknown option " + opt); return showUsage(); diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index eaea98958e45..05f49c5a1f02 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -39,6 +39,7 @@ import android.media.AudioManager; import android.media.session.MediaSession; import android.net.Uri; import android.os.BadParcelableException; +import android.os.BaseBundle; import android.os.Build; import android.os.Bundle; import android.os.Parcel; @@ -53,6 +54,7 @@ import android.text.style.AbsoluteSizeSpan; import android.text.style.CharacterStyle; import android.text.style.RelativeSizeSpan; import android.text.style.TextAppearanceSpan; +import android.util.ArraySet; import android.util.Log; import android.util.SparseArray; import android.view.Gravity; @@ -63,6 +65,7 @@ import android.widget.ProgressBar; import android.widget.RemoteViews; import com.android.internal.R; +import com.android.internal.util.ArrayUtils; import com.android.internal.util.NotificationColorUtil; import java.lang.annotation.Retention; @@ -70,6 +73,7 @@ import java.lang.annotation.RetentionPolicy; import java.lang.reflect.Constructor; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Set; @@ -758,6 +762,16 @@ public class Notification implements Parcelable public Bundle extras = new Bundle(); /** + * All pending intents in the notification extras (notification extras, actions extras, + * and remote input extras) as the system needs to be able to access them but touching + * the extras bundle in the system process is not safe because the bundle may contain + * custom parcelable objects. + * + * @hide + */ + public ArraySet<PendingIntent> extrasPendingIntents; + + /** * {@link #extras} key: this is the title of the notification, * as supplied to {@link Builder#setContentTitle(CharSequence)}. */ @@ -1573,7 +1587,16 @@ public class Notification implements Parcelable /** * Unflatten the notification from a parcel. */ - public Notification(Parcel parcel) + @SuppressWarnings("unchecked") + public Notification(Parcel parcel) { + // IMPORTANT: Add unmarshaling code in readFromParcel as the pending + // intents in extras are always written as the last entry. + readFromParcelImpl(parcel); + // Must be read last! + extrasPendingIntents = (ArraySet<PendingIntent>) parcel.readArraySet(null); + } + + private void readFromParcelImpl(Parcel parcel) { int version = parcel.readInt(); @@ -1728,6 +1751,10 @@ public class Notification implements Parcelable } } + if (!ArrayUtils.isEmpty(extrasPendingIntents)) { + that.extrasPendingIntents = new ArraySet<>(extrasPendingIntents); + } + if (this.actions != null) { that.actions = new Action[this.actions.length]; for(int i=0; i<this.actions.length; i++) { @@ -1843,8 +1870,40 @@ public class Notification implements Parcelable /** * Flatten this notification into a parcel. */ - public void writeToParcel(Parcel parcel, int flags) - { + public void writeToParcel(Parcel parcel, int flags) { + // We need to mark all pending intents getting into the notification + // system as being put there to later allow the notification ranker + // to launch them and by doing so add the app to the battery saver white + // list for a short period of time. The problem is that the system + // cannot look into the extras as there may be parcelables there that + // the platform does not know how to handle. To go around that we have + // an explicit list of the pending intents in the extras bundle. + final boolean collectPendingIntents = (extrasPendingIntents == null); + if (collectPendingIntents) { + PendingIntent.setOnMarshaledListener( + (PendingIntent intent, Parcel out, int outFlags) -> { + if (parcel == out) { + if (extrasPendingIntents == null) { + extrasPendingIntents = new ArraySet<>(); + } + extrasPendingIntents.add(intent); + } + }); + } + try { + // IMPORTANT: Add marshaling code in writeToParcelImpl as we + // want to intercept all pending events written to the pacel. + writeToParcelImpl(parcel, flags); + // Must be written last! + parcel.writeArraySet(extrasPendingIntents); + } finally { + if (collectPendingIntents) { + PendingIntent.setOnMarshaledListener(null); + } + } + } + + private void writeToParcelImpl(Parcel parcel, int flags) { parcel.writeInt(1); parcel.writeLong(when); diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java index cb15392b6a8a..cfa242be02aa 100644 --- a/core/java/android/app/PendingIntent.java +++ b/core/java/android/app/PendingIntent.java @@ -242,6 +242,36 @@ public final class PendingIntent implements Parcelable { } /** + * Listener for observing when pending intents are written to a parcel. + * + * @hide + */ + public interface OnMarshaledListener { + /** + * Called when a pending intent is written to a parcel. + * + * @param intent The pending intent. + * @param parcel The parcel to which it was written. + * @param flags The parcel flags when it was written. + */ + void onMarshaled(PendingIntent intent, Parcel parcel, int flags); + } + + private static final ThreadLocal<OnMarshaledListener> sOnMarshaledListener + = new ThreadLocal<>(); + + /** + * Registers an listener for pending intents being written to a parcel. + * + * @param listener The listener, null to clear. + * + * @hide + */ + public static void setOnMarshaledListener(OnMarshaledListener listener) { + sOnMarshaledListener.set(listener); + } + + /** * Retrieve a PendingIntent that will start a new activity, like calling * {@link Context#startActivity(Intent) Context.startActivity(Intent)}. * Note that the activity will be started outside of the context of an @@ -1016,6 +1046,11 @@ public final class PendingIntent implements Parcelable { public void writeToParcel(Parcel out, int flags) { out.writeStrongBinder(mTarget.asBinder()); + OnMarshaledListener listener = sOnMarshaledListener.get(); + if (listener != null) { + listener.onMarshaled(this, out, flags); + } + } public static final Parcelable.Creator<PendingIntent> CREATOR diff --git a/core/java/android/content/pm/IOtaDexopt.aidl b/core/java/android/content/pm/IOtaDexopt.aidl index 786a77f64110..467bd5f5e82b 100644 --- a/core/java/android/content/pm/IOtaDexopt.aidl +++ b/core/java/android/content/pm/IOtaDexopt.aidl @@ -50,6 +50,13 @@ interface IOtaDexopt { /** * Optimize the next package. Note: this command is synchronous, that is, only returns after * the package has been dexopted (or dexopting failed). + * + * Note: this will be removed after a transition period. Use nextDexoptCommand instead. */ void dexoptNextPackage(); + + /** + * Get the optimization parameters for the next package. + */ + String nextDexoptCommand(); } diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java index 26312475297c..74dcc0787b3b 100644 --- a/core/java/android/os/Parcel.java +++ b/core/java/android/os/Parcel.java @@ -17,8 +17,10 @@ package android.os; import android.annotation.IntegerRes; +import android.annotation.Nullable; import android.text.TextUtils; import android.util.ArrayMap; +import android.util.ArraySet; import android.util.Log; import android.util.Size; import android.util.SizeF; @@ -734,6 +736,21 @@ public final class Parcel { } /** + * Write an array set to the parcel. + * + * @param val The array set to write. + * + * @hide + */ + public void writeArraySet(@Nullable ArraySet<? extends Object> val) { + final int size = (val != null) ? val.size() : -1; + writeInt(size); + for (int i = 0; i < size; i++) { + writeValue(val.valueAt(i)); + } + } + + /** * Flatten a Bundle into the parcel at the current dataPosition(), * growing dataCapacity() if needed. */ @@ -2735,6 +2752,26 @@ public final class Parcel { readArrayMapInternal(outVal, N, loader); } + /** + * Reads an array set. + * + * @param loader The class loader to use. + * + * @hide + */ + public @Nullable ArraySet<? extends Object> readArraySet(ClassLoader loader) { + final int size = readInt(); + if (size < 0) { + return null; + } + ArraySet<Object> result = new ArraySet<>(size); + for (int i = 0; i < size; i++) { + Object value = readValue(loader); + result.append(value); + } + return result; + } + private void readListInternal(List outVal, int N, ClassLoader loader) { while (N > 0) { diff --git a/core/java/android/util/ArraySet.java b/core/java/android/util/ArraySet.java index 9e9314fba4c4..d39e91fd98b2 100644 --- a/core/java/android/util/ArraySet.java +++ b/core/java/android/util/ArraySet.java @@ -390,6 +390,32 @@ public final class ArraySet<E> implements Collection<E>, Set<E> { } /** + * Special fast path for appending items to the end of the array without validation. + * The array must already be large enough to contain the item. + * @hide + */ + public void append(E value) { + final int index = mSize; + final int hash = value == null ? 0 + : (mIdentityHashCode ? System.identityHashCode(value) : value.hashCode()); + if (index >= mHashes.length) { + 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); + add(value); + return; + } + mSize = index + 1; + mHashes[index] = hash; + mArray[index] = value; + } + + /** * Perform a {@link #add(Object)} of all values in <var>array</var> * @param array The array whose contents are to be retrieved. */ diff --git a/libs/hwui/LayerUpdateQueue.cpp b/libs/hwui/LayerUpdateQueue.cpp index db5f676d09dc..95f5cfb33474 100644 --- a/libs/hwui/LayerUpdateQueue.cpp +++ b/libs/hwui/LayerUpdateQueue.cpp @@ -26,6 +26,7 @@ void LayerUpdateQueue::clear() { } void LayerUpdateQueue::enqueueLayerWithDamage(RenderNode* renderNode, Rect damage) { + damage.roundOut(); damage.doIntersect(0, 0, renderNode->getWidth(), renderNode->getHeight()); if (!damage.isEmpty()) { for (Entry& entry : mEntries) { diff --git a/libs/hwui/tests/unit/LayerUpdateQueueTests.cpp b/libs/hwui/tests/unit/LayerUpdateQueueTests.cpp index 8b0e91c77396..4db1cb935902 100644 --- a/libs/hwui/tests/unit/LayerUpdateQueueTests.cpp +++ b/libs/hwui/tests/unit/LayerUpdateQueueTests.cpp @@ -39,17 +39,21 @@ static sp<RenderNode> createSyncedNode(uint32_t width, uint32_t height) { TEST(LayerUpdateQueue, enqueueSimple) { sp<RenderNode> a = createSyncedNode(100, 100); sp<RenderNode> b = createSyncedNode(200, 200); + sp<RenderNode> c = createSyncedNode(200, 200); LayerUpdateQueue queue; queue.enqueueLayerWithDamage(a.get(), Rect(25, 25, 75, 75)); queue.enqueueLayerWithDamage(b.get(), Rect(100, 100, 300, 300)); + queue.enqueueLayerWithDamage(c.get(), Rect(.5, .5, .5, .5)); - EXPECT_EQ(2u, queue.entries().size()); + EXPECT_EQ(3u, queue.entries().size()); EXPECT_EQ(a.get(), queue.entries()[0].renderNode); EXPECT_EQ(Rect(25, 25, 75, 75), queue.entries()[0].damage); EXPECT_EQ(b.get(), queue.entries()[1].renderNode); EXPECT_EQ(Rect(100, 100, 200, 200), queue.entries()[1].damage); // clipped to bounds + EXPECT_EQ(c.get(), queue.entries()[2].renderNode); + EXPECT_EQ(Rect(0, 0, 1, 1), queue.entries()[2].damage); // rounded out } TEST(LayerUpdateQueue, enqueueUnion) { diff --git a/packages/MtpDocumentsProvider/res/values-af/strings.xml b/packages/MtpDocumentsProvider/res/values-af/strings.xml new file mode 100644 index 000000000000..c2c8761146f3 --- /dev/null +++ b/packages/MtpDocumentsProvider/res/values-af/strings.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2015 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="app_label" msgid="6271216747302322594">"MTP-gasheer"</string> + <string name="downloads_app_label" msgid="7120690641874849726">"Aflaaie"</string> + <string name="root_name" msgid="5819495383921089536">"<xliff:g id="DEVICE_MODEL">%1$s</xliff:g> <xliff:g id="STORAGE_NAME">%2$s</xliff:g>"</string> + <string name="accessing_notification_title" msgid="3030133609230917944">"Toegang tot lêers word tans van <xliff:g id="DEVICE_MODEL">%1$s</xliff:g> af verkry"</string> + <string name="error_busy_device" msgid="3997316850357386589">"Die ander toestel is besig. Jy kan nie lêers oordra voordat dit beskikbaar is nie."</string> + <string name="error_locked_device" msgid="7557872102188356147">"Geen lêers is gevind nie. Die ander toestel is dalk gesluit. Indien wel, ontsluit dit en probeer weer."</string> +</resources> diff --git a/packages/MtpDocumentsProvider/res/values-sv/strings.xml b/packages/MtpDocumentsProvider/res/values-sv/strings.xml new file mode 100644 index 000000000000..26818eb220ce --- /dev/null +++ b/packages/MtpDocumentsProvider/res/values-sv/strings.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Copyright (C) 2015 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> + +<resources xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="app_label" msgid="6271216747302322594">"MTP-värd"</string> + <string name="downloads_app_label" msgid="7120690641874849726">"Nedladdningar"</string> + <string name="root_name" msgid="5819495383921089536">"<xliff:g id="DEVICE_MODEL">%1$s</xliff:g> <xliff:g id="STORAGE_NAME">%2$s</xliff:g>"</string> + <string name="accessing_notification_title" msgid="3030133609230917944">"Åtkomst till filer från <xliff:g id="DEVICE_MODEL">%1$s</xliff:g>"</string> + <string name="error_busy_device" msgid="3997316850357386589">"Den andra enheten är upptagen. Du kan inte överföra filer förrän den är tillgänglig."</string> + <string name="error_locked_device" msgid="7557872102188356147">"Inga filer hittades. Den andra enheten kan vara låst. Om den är det låser du upp den och försöker igen."</string> +</resources> diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java index d3f5d2667ebf..569a567c25fa 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java @@ -82,8 +82,11 @@ public class CustomTile extends QSTile<QSTile.State> implements TileChangeListen private void setTileIcon() { try { PackageManager pm = mContext.getPackageManager(); - ServiceInfo info = pm.getServiceInfo(mComponent, - PackageManager.MATCH_ENCRYPTION_AWARE_AND_UNAWARE); + int flags = PackageManager.MATCH_ENCRYPTION_AWARE_AND_UNAWARE; + if (isSystemApp(pm)) { + flags |= PackageManager.MATCH_DISABLED_COMPONENTS; + } + ServiceInfo info = pm.getServiceInfo(mComponent, flags); int icon = info.icon != 0 ? info.icon : info.applicationInfo.icon; // Update the icon if its not set or is the default icon. @@ -103,6 +106,10 @@ public class CustomTile extends QSTile<QSTile.State> implements TileChangeListen } } + private boolean isSystemApp(PackageManager pm) throws PackageManager.NameNotFoundException { + return pm.getApplicationInfo(mComponent.getPackageName(), 0).isSystemApp(); + } + /** * Compare two icons, only works for resources. */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java index d5ff0b356d6d..42f398d8d83f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java @@ -153,7 +153,8 @@ public class KeyguardStatusBarView extends RelativeLayout if (mKeyguardUserSwitcher == null) { // If we have no keyguard switcher, the screen width is under 600dp. In this case, // we don't show the multi-user avatar unless there is more than 1 user on the device. - if (mUserSwitcherController.getSwitchableUserCount() > 1) { + if (mUserSwitcherController != null + && mUserSwitcherController.getSwitchableUserCount() > 1) { mMultiUserSwitch.setVisibility(View.VISIBLE); } else { mMultiUserSwitch.setVisibility(View.GONE); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java index 5eaea90479ce..87e87c8e9bb3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/StackScrollAlgorithm.java @@ -154,8 +154,8 @@ public class StackScrollAlgorithm { float newNotificationEnd = newYTranslation + newHeight; boolean isHeadsUp = (child instanceof ExpandableNotificationRow) && ((ExpandableNotificationRow) child).isPinned(); - if (newYTranslation < previousNotificationEnd && ambientState.isShadeExpanded() - && !isHeadsUp) { + if (newYTranslation < previousNotificationEnd + && (!isHeadsUp || ambientState.isShadeExpanded())) { // The previous view is overlapping on top, clip! float overlapAmount = previousNotificationEnd - newYTranslation; state.clipTopAmount = (int) overlapAmount; diff --git a/services/core/java/com/android/server/AttributeCache.java b/services/core/java/com/android/server/AttributeCache.java index 57f18c086c56..58ec836547a7 100644 --- a/services/core/java/com/android/server/AttributeCache.java +++ b/services/core/java/com/android/server/AttributeCache.java @@ -25,23 +25,24 @@ import android.content.res.Resources; import android.content.res.TypedArray; import android.os.UserHandle; import android.util.ArrayMap; +import android.util.LruCache; import android.util.SparseArray; import com.android.internal.annotations.GuardedBy; -import java.lang.ref.WeakReference; - /** * TODO: This should be better integrated into the system so it doesn't need * special calls from the activity manager to clear it. */ public final class AttributeCache { + private static final int CACHE_SIZE = 4; private static AttributeCache sInstance = null; private final Context mContext; @GuardedBy("this") - private final ArrayMap<String, WeakReference<Package>> mPackages = new ArrayMap<>(); + private final LruCache<String, Package> mPackages = new LruCache<>(CACHE_SIZE); + @GuardedBy("this") private final Configuration mConfiguration = new Configuration(); @@ -86,15 +87,12 @@ public final class AttributeCache { public void removePackage(String packageName) { synchronized (this) { - final WeakReference<Package> ref = mPackages.remove(packageName); - final Package pkg = (ref != null) ? ref.get() : null; + final Package pkg = mPackages.remove(packageName); if (pkg != null) { - if (pkg.mMap != null) { - for (int i = 0; i < pkg.mMap.size(); i++) { - final ArrayMap<int[], Entry> map = pkg.mMap.valueAt(i); - for (int j = 0; j < map.size(); j++) { - map.valueAt(j).recycle(); - } + for (int i = 0; i < pkg.mMap.size(); i++) { + final ArrayMap<int[], Entry> map = pkg.mMap.valueAt(i); + for (int j = 0; j < map.size(); j++) { + map.valueAt(j).recycle(); } } @@ -113,15 +111,14 @@ public final class AttributeCache { // The configurations being masked out are ones that commonly // change so we don't want flushing the cache... all others // will flush the cache. - mPackages.clear(); + mPackages.evictAll(); } } } public Entry get(String packageName, int resId, int[] styleable, int userId) { synchronized (this) { - WeakReference<Package> ref = mPackages.get(packageName); - Package pkg = (ref != null) ? ref.get() : null; + Package pkg = mPackages.get(packageName); ArrayMap<int[], Entry> map = null; Entry ent = null; if (pkg != null) { @@ -144,7 +141,7 @@ public final class AttributeCache { return null; } pkg = new Package(context); - mPackages.put(packageName, new WeakReference<>(pkg)); + mPackages.put(packageName, pkg); } if (map == null) { diff --git a/services/core/java/com/android/server/BluetoothManagerService.java b/services/core/java/com/android/server/BluetoothManagerService.java index baa0bbe3e4fb..8c5887f7a514 100644 --- a/services/core/java/com/android/server/BluetoothManagerService.java +++ b/services/core/java/com/android/server/BluetoothManagerService.java @@ -61,7 +61,7 @@ import java.util.Map; class BluetoothManagerService extends IBluetoothManager.Stub { private static final String TAG = "BluetoothManagerService"; - private static final boolean DBG = false; + private static final boolean DBG = true; private static final String BLUETOOTH_ADMIN_PERM = android.Manifest.permission.BLUETOOTH_ADMIN; private static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH; @@ -1512,7 +1512,7 @@ class BluetoothManagerService extends IBluetoothManager.Stub { bluetoothStateChangeHandler(BluetoothAdapter.STATE_ON, BluetoothAdapter.STATE_TURNING_OFF); - waitForOnOff(false, true); + boolean didDisableTimeout = !waitForOnOff(false, true); bluetoothStateChangeHandler(BluetoothAdapter.STATE_TURNING_OFF, BluetoothAdapter.STATE_OFF); @@ -1530,7 +1530,16 @@ class BluetoothManagerService extends IBluetoothManager.Stub { mBluetoothLock.writeLock().unlock(); } - SystemClock.sleep(100); + // + // If disabling Bluetooth times out, wait for an + // additional amount of time to ensure the process is + // shut down completely before attempting to restart. + // + if (didDisableTimeout) { + SystemClock.sleep(3000); + } else { + SystemClock.sleep(100); + } mHandler.removeMessages(MESSAGE_BLUETOOTH_STATE_CHANGE); mState = BluetoothAdapter.STATE_OFF; diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java index 798662910cbf..d9148817881b 100755 --- a/services/core/java/com/android/server/am/ActiveServices.java +++ b/services/core/java/com/android/server/am/ActiveServices.java @@ -43,7 +43,6 @@ import android.os.TransactionTooLargeException; import android.util.ArrayMap; import android.util.ArraySet; -import com.android.internal.app.procstats.ProcessStats; import com.android.internal.app.procstats.ServiceState; import com.android.internal.os.BatteryStatsImpl; import com.android.internal.os.TransferPipe; @@ -751,6 +750,17 @@ public final class ActiveServices { mAm.updateProcessForegroundLocked(proc, anyForeground, oomAdj); } + private void updateWhitelistManagerLocked(ProcessRecord proc) { + proc.whitelistManager = false; + for (int i=proc.services.size()-1; i>=0; i--) { + ServiceRecord sr = proc.services.valueAt(i); + if (sr.whitelistManager) { + proc.whitelistManager = true; + break; + } + } + } + public void updateServiceConnectionActivitiesLocked(ProcessRecord clientProc) { ArraySet<ProcessRecord> updatedProcesses = null; for (int i = 0; i < clientProc.connections.size(); i++) { @@ -997,6 +1007,9 @@ public final class ActiveServices { if ((c.flags&Context.BIND_ABOVE_CLIENT) != 0) { b.client.hasAboveClient = true; } + if ((c.flags&Context.BIND_ALLOW_WHITELIST_MANAGEMENT) != 0) { + s.whitelistManager = true; + } if (s.app != null) { updateServiceClientActivitiesLocked(s.app, c, true); } @@ -1019,6 +1032,9 @@ public final class ActiveServices { if ((flags&Context.BIND_TREAT_LIKE_ACTIVITY) != 0) { s.app.treatLikeActivity = true; } + if (s.whitelistManager) { + s.app.whitelistManager = true; + } // This could have made the service more important. mAm.updateLruProcessLocked(s.app, s.app.hasClientActivities || s.app.treatLikeActivity, b.client); @@ -1807,6 +1823,10 @@ public final class ActiveServices { } } + if (r.whitelistManager) { + app.whitelistManager = true; + } + requestServiceBindingsLocked(r, execInFg); updateServiceClientActivitiesLocked(app, null, true); @@ -2018,6 +2038,9 @@ public final class ActiveServices { r.stats.stopLaunchedLocked(); } r.app.services.remove(r); + if (r.whitelistManager) { + updateWhitelistManagerLocked(r.app); + } if (r.app.thread != null) { updateServiceForegroundLocked(r.app, false); try { @@ -2085,6 +2108,14 @@ public final class ActiveServices { if ((c.flags&Context.BIND_ABOVE_CLIENT) != 0) { b.client.updateHasAboveClientLocked(); } + // If this connection requested whitelist management, see if we should + // now clear that state. + if ((c.flags&Context.BIND_ALLOW_WHITELIST_MANAGEMENT) != 0) { + s.updateWhitelistManager(); + if (!s.whitelistManager && s.app != null) { + updateWhitelistManagerLocked(s.app); + } + } if (s.app != null) { updateServiceClientActivitiesLocked(s.app, c, true); } @@ -2284,6 +2315,9 @@ public final class ActiveServices { if (finishing) { if (r.app != null && !r.app.persistent) { r.app.services.remove(r); + if (r.whitelistManager) { + updateWhitelistManagerLocked(r.app); + } } r.app = null; } @@ -2379,6 +2413,9 @@ public final class ActiveServices { service.app.removed = killProcess; if (!service.app.persistent) { service.app.services.remove(service); + if (service.whitelistManager) { + updateWhitelistManagerLocked(service.app); + } } } service.app = null; @@ -2497,6 +2534,8 @@ public final class ActiveServices { updateServiceConnectionActivitiesLocked(app); app.connections.clear(); + app.whitelistManager = false; + // Clear app state from services. for (int i = app.services.size() - 1; i >= 0; i--) { ServiceRecord sr = app.services.valueAt(i); diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 02d6a42dc5d5..5893f9018631 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -19294,7 +19294,6 @@ public final class ActivityManagerService extends ActivityManagerNative } boolean mayBeTop = false; - app.whitelistManager = false; for (int is = app.services.size()-1; is >= 0 && (adj > ProcessList.FOREGROUND_APP_ADJ @@ -19353,9 +19352,6 @@ public final class ActivityManagerService extends ActivityManagerNative // Binding to ourself is not interesting. continue; } - if ((cr.flags & Context.BIND_ALLOW_WHITELIST_MANAGEMENT) != 0) { - app.whitelistManager = true; - } if ((cr.flags&Context.BIND_WAIVE_PRIORITY) == 0) { ProcessRecord client = cr.binding.client; diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java index 8fb10fdc1ea5..6d229466ebf9 100644 --- a/services/core/java/com/android/server/am/ActivityStack.java +++ b/services/core/java/com/android/server/am/ActivityStack.java @@ -1706,7 +1706,8 @@ final class ActivityStack { final boolean stackInvisible = stackVisibility != STACK_VISIBLE; final boolean stackVisibleBehind = stackVisibility == STACK_VISIBLE_ACTIVITY_BEHIND; boolean behindFullscreenActivity = stackInvisible; - boolean resumeNextActivity = isFocusable() && (isInStackLocked(starting) == null); + boolean resumeNextActivity = mStackSupervisor.isFocusedStack(this) + && (isInStackLocked(starting) == null); boolean behindTranslucentActivity = false; final ActivityRecord visibleBehind = getVisibleBehindActivity(); for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) { diff --git a/services/core/java/com/android/server/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java index 0a081e9809ea..2bfc4021f887 100644 --- a/services/core/java/com/android/server/am/ServiceRecord.java +++ b/services/core/java/com/android/server/am/ServiceRecord.java @@ -90,6 +90,7 @@ final class ServiceRecord extends Binder { ProcessRecord isolatedProc; // keep track of isolated process, if requested ServiceState tracker; // tracking service execution, may be null ServiceState restartTracker; // tracking service restart + boolean whitelistManager; // any bindings to this service have BIND_ALLOW_WHITELIST_MANAGEMENT? boolean delayed; // are we waiting to start this service in the background? boolean isForeground; // is service currently in foreground mode? int foregroundId; // Notification ID of last foreground req. @@ -225,6 +226,9 @@ final class ServiceRecord extends Binder { if (isolatedProc != null) { pw.print(prefix); pw.print("isolatedProc="); pw.println(isolatedProc); } + if (whitelistManager) { + pw.print(prefix); pw.print("whitelistManager="); pw.println(whitelistManager); + } if (delayed) { pw.print(prefix); pw.print("delayed="); pw.println(delayed); } @@ -391,6 +395,19 @@ final class ServiceRecord extends Binder { return false; } + public void updateWhitelistManager() { + whitelistManager = false; + for (int conni=connections.size()-1; conni>=0; conni--) { + ArrayList<ConnectionRecord> cr = connections.valueAt(conni); + for (int i=0; i<cr.size(); i++) { + if ((cr.get(i).flags&Context.BIND_ALLOW_WHITELIST_MANAGEMENT) != 0) { + whitelistManager = true; + return; + } + } + } + } + public void resetRestartCounter() { restartCount = 0; restartDelay = 0; diff --git a/services/core/java/com/android/server/job/controllers/ContentObserverController.java b/services/core/java/com/android/server/job/controllers/ContentObserverController.java index 9dce070221bc..a42d0cd4c831 100644 --- a/services/core/java/com/android/server/job/controllers/ContentObserverController.java +++ b/services/core/java/com/android/server/job/controllers/ContentObserverController.java @@ -22,6 +22,7 @@ import android.database.ContentObserver; import android.net.Uri; import android.os.Handler; import android.os.UserHandle; +import android.util.Slog; import android.util.TimeUtils; import android.util.ArrayMap; import android.util.ArraySet; @@ -40,6 +41,7 @@ import java.util.List; */ public class ContentObserverController extends StateController { private static final String TAG = "JobScheduler.Content"; + private static final boolean DEBUG = false; /** * Maximum number of changing URIs we will batch together to report. @@ -88,6 +90,9 @@ public class ContentObserverController extends StateController { if (taskStatus.contentObserverJobInstance == null) { taskStatus.contentObserverJobInstance = new JobInstance(taskStatus); } + if (DEBUG) { + Slog.i(TAG, "Tracking content-trigger job " + taskStatus); + } mTrackedTasks.add(taskStatus); boolean havePendingUris = false; // If there is a previous job associated with the new job, propagate over @@ -175,6 +180,9 @@ public class ContentObserverController extends StateController { taskStatus.contentObserverJobInstance = null; } } + if (DEBUG) { + Slog.i(TAG, "No longer tracking job " + taskStatus); + } mTrackedTasks.remove(taskStatus); } } @@ -194,16 +202,20 @@ public class ContentObserverController extends StateController { } final class ObserverInstance extends ContentObserver { - final Uri mUri; + final JobInfo.TriggerContentUri mUri; final ArraySet<JobInstance> mJobs = new ArraySet<>(); - public ObserverInstance(Handler handler, Uri uri) { + public ObserverInstance(Handler handler, JobInfo.TriggerContentUri uri) { super(handler); mUri = uri; } @Override public void onChange(boolean selfChange, Uri uri) { + if (DEBUG) { + Slog.i(TAG, "onChange(self=" + selfChange + ") for " + uri + + " when mUri=" + mUri); + } synchronized (mLock) { final int N = mJobs.size(); for (int i=0; i<N; i++) { @@ -255,14 +267,25 @@ public class ContentObserverController extends StateController { for (JobInfo.TriggerContentUri uri : uris) { ObserverInstance obs = mObservers.get(uri); if (obs == null) { - obs = new ObserverInstance(mHandler, uri.getUri()); + obs = new ObserverInstance(mHandler, uri); mObservers.put(uri, obs); + final boolean andDescendants = (uri.getFlags() & + JobInfo.TriggerContentUri.FLAG_NOTIFY_FOR_DESCENDANTS) != 0; + if (DEBUG) { + Slog.v(TAG, "New observer " + obs + " for " + uri.getUri() + + " andDescendants=" + andDescendants); + } mContext.getContentResolver().registerContentObserver( uri.getUri(), - (uri.getFlags() & - JobInfo.TriggerContentUri.FLAG_NOTIFY_FOR_DESCENDANTS) - != 0, + andDescendants, obs); + } else { + if (DEBUG) { + final boolean andDescendants = (uri.getFlags() & + JobInfo.TriggerContentUri.FLAG_NOTIFY_FOR_DESCENDANTS) != 0; + Slog.v(TAG, "Reusing existing observer " + obs + " for " + uri.getUri() + + " andDescendants=" + andDescendants); + } } obs.mJobs.add(this); mMyObservers.add(obs); @@ -315,8 +338,11 @@ public class ContentObserverController extends StateController { final ObserverInstance obs = mMyObservers.get(i); obs.mJobs.remove(this); if (obs.mJobs.size() == 0) { + if (DEBUG) { + Slog.i(TAG, "Unregistering observer " + obs + " for " + obs.mUri.getUri()); + } mContext.getContentResolver().unregisterContentObserver(obs); - mObservers.remove(obs); + mObservers.remove(obs.mUri); } } } diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 59de263946a6..9209d3d4b54e 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -2598,7 +2598,6 @@ public class NotificationManagerService extends SystemService { final long duration = LocalServices.getService(DeviceIdleController.LocalService.class) .getNotificationWhitelistDuration(); - int size = 0; if (notification.contentIntent != null) { am.setPendingIntentWhitelistDuration(notification.contentIntent.getTarget(), duration); } @@ -2615,45 +2614,19 @@ public class NotificationManagerService extends SystemService { continue; } am.setPendingIntentWhitelistDuration(action.actionIntent.getTarget(), duration); - setPendingIntentWhitelistDuration(am, duration, action.getExtras()); - final RemoteInput[] remoteInputs = action.getRemoteInputs(); - if (remoteInputs != null) { - for (RemoteInput remoteInput : remoteInputs) { - setPendingIntentWhitelistDuration(am, duration, remoteInput.getExtras()); - } - } } } - } - - private static void setPendingIntentWhitelistDuration(ActivityManagerInternal am, long duration, - Bundle extras) { - for (String key : extras.keySet()) { - final Object value = extras.get(key); - if (value instanceof Parcelable) { - setPendingIntentWhitelistDuration(am, duration, (Parcelable) value); - } else if (value instanceof Parcelable[]) { - for (Parcelable parcelable : (Parcelable[]) value) { - setPendingIntentWhitelistDuration(am, duration, parcelable); - } - } else if (value instanceof List) { - for (Object element : (List <?>) value) { - if (element instanceof Parcelable) { - setPendingIntentWhitelistDuration(am, duration, (Parcelable) element); - } + if (notification.extrasPendingIntents != null) { + final int intentCount = notification.extrasPendingIntents.size(); + for (int i = 0; i < intentCount; i++) { + PendingIntent pendingIntent = notification.extrasPendingIntents.valueAt(i); + if (pendingIntent != null) { + am.setPendingIntentWhitelistDuration(pendingIntent.getTarget(), duration); } } } } - private static void setPendingIntentWhitelistDuration(ActivityManagerInternal am, long duration, - Parcelable parcelable) { - if (parcelable instanceof PendingIntent) { - am.setPendingIntentWhitelistDuration(((PendingIntent) parcelable).getTarget(), - duration); - } - } - private class EnqueueNotificationRunnable implements Runnable { private final NotificationRecord r; private final int userId; diff --git a/services/core/java/com/android/server/pm/Installer.java b/services/core/java/com/android/server/pm/Installer.java index 7b85a4f25cee..72c549f7bec6 100644 --- a/services/core/java/com/android/server/pm/Installer.java +++ b/services/core/java/com/android/server/pm/Installer.java @@ -61,6 +61,13 @@ public final class Installer extends SystemService { mInstaller = new InstallerConnection(); } + // Package-private installer that accepts a custom InstallerConnection. Used for + // OtaDexoptService. + Installer(Context context, InstallerConnection connection) { + super(context); + mInstaller = connection; + } + /** * Yell loudly if someone tries making future calls while holding a lock on * the given object. diff --git a/services/core/java/com/android/server/pm/OtaDexoptService.java b/services/core/java/com/android/server/pm/OtaDexoptService.java index df91f4a1f62a..01b3dc28b50e 100644 --- a/services/core/java/com/android/server/pm/OtaDexoptService.java +++ b/services/core/java/com/android/server/pm/OtaDexoptService.java @@ -32,10 +32,12 @@ import android.os.storage.StorageManager; import android.util.Log; import android.util.Slog; +import com.android.internal.os.InstallerConnection; import com.android.internal.os.InstallerConnection.InstallerException; import java.io.File; import java.io.FileDescriptor; +import java.util.ArrayList; import java.util.Collection; import java.util.List; @@ -49,21 +51,28 @@ public class OtaDexoptService extends IOtaDexopt.Stub { private final static boolean DEBUG_DEXOPT = true; private final Context mContext; - private final PackageDexOptimizer mPackageDexOptimizer; private final PackageManagerService mPackageManagerService; // TODO: Evaluate the need for WeakReferences here. + + /** + * The list of packages to dexopt. + */ private List<PackageParser.Package> mDexoptPackages; + + /** + * The list of dexopt invocations for the current package (which will no longer be in + * mDexoptPackages). This can be more than one as a package may have multiple code paths, + * e.g., in the split-APK case. + */ + private List<String> mCommandsForCurrentPackage; + private int completeSize; public OtaDexoptService(Context context, PackageManagerService packageManagerService) { this.mContext = context; this.mPackageManagerService = packageManagerService; - // Use the package manager install and install lock here for the OTA dex optimizer. - mPackageDexOptimizer = new OTADexoptPackageDexOptimizer(packageManagerService.mInstaller, - packageManagerService.mInstallLock, context); - // Now it's time to check whether we need to move any A/B artifacts. moveAbArtifacts(packageManagerService.mInstaller); } @@ -93,6 +102,7 @@ public class OtaDexoptService extends IOtaDexopt.Stub { mPackageManagerService.mPackages.values(), mPackageManagerService); } completeSize = mDexoptPackages.size(); + mCommandsForCurrentPackage = null; } @Override @@ -101,6 +111,7 @@ public class OtaDexoptService extends IOtaDexopt.Stub { Log.i(TAG, "Cleaning up OTA Dexopt state."); } mDexoptPackages = null; + mCommandsForCurrentPackage = null; } @Override @@ -109,15 +120,109 @@ public class OtaDexoptService extends IOtaDexopt.Stub { throw new IllegalStateException("done() called before prepare()"); } - return mDexoptPackages.isEmpty(); + return mDexoptPackages.isEmpty() && (mCommandsForCurrentPackage == null); } @Override public synchronized float getProgress() throws RemoteException { + // We approximate by number of packages here. We could track all compiles, if we + // generated them ahead of time. Right now we're trying to conserve memory. if (completeSize == 0) { return 1f; } - return (completeSize - mDexoptPackages.size()) / ((float)completeSize); + int packagesLeft = mDexoptPackages.size() + (mCommandsForCurrentPackage != null ? 1 : 0); + return (completeSize - packagesLeft) / ((float)completeSize); + } + + /** + * Return the next dexopt command for the current package. Enforces the invariant + */ + private String getNextPackageDexopt() { + if (mCommandsForCurrentPackage != null) { + String next = mCommandsForCurrentPackage.remove(0); + if (mCommandsForCurrentPackage.isEmpty()) { + mCommandsForCurrentPackage = null; + } + return next; + } + return null; + } + + @Override + public synchronized String nextDexoptCommand() throws RemoteException { + if (mDexoptPackages == null) { + throw new IllegalStateException("dexoptNextPackage() called before prepare()"); + } + + // Get the next command. + for (;;) { + // Check whether there's one for the current package. + String next = getNextPackageDexopt(); + if (next != null) { + return next; + } + + // Move to the next package, if possible. + if (mDexoptPackages.isEmpty()) { + return "Nothing to do"; + } + + PackageParser.Package nextPackage = mDexoptPackages.remove(0); + + if (DEBUG_DEXOPT) { + Log.i(TAG, "Processing " + nextPackage.packageName + " for OTA dexopt."); + } + + // Generate the next mPackageDexopts state. Ignore errors, this loop is strongly + // monotonically increasing, anyways. + generatePackageDexopts(nextPackage); + + // Invariant check: mPackageDexopts is null or not empty. + if (mCommandsForCurrentPackage != null && mCommandsForCurrentPackage.isEmpty()) { + cleanup(); + throw new IllegalStateException("mPackageDexopts empty for " + nextPackage); + } + } + } + + /** + * Generate all dexopt commands for the given package and place them into mPackageDexopts. + * Returns true on success, false in an error situation like low disk space. + */ + private synchronized boolean generatePackageDexopts(PackageParser.Package nextPackage) { + // Check for low space. + // TODO: If apps are not installed in the internal /data partition, we should compare + // against that storage's free capacity. + File dataDir = Environment.getDataDirectory(); + @SuppressWarnings("deprecation") + long lowThreshold = StorageManager.from(mContext).getStorageLowBytes(dataDir); + if (lowThreshold == 0) { + throw new IllegalStateException("Invalid low memory threshold"); + } + long usableSpace = dataDir.getUsableSpace(); + if (usableSpace < lowThreshold) { + Log.w(TAG, "Not running dexopt on " + nextPackage.packageName + " due to low memory: " + + usableSpace); + return false; + } + + // Use our custom connection that just collects the commands. + RecordingInstallerConnection collectingConnection = new RecordingInstallerConnection(); + Installer collectingInstaller = new Installer(mContext, collectingConnection); + + // Use the package manager install and install lock here for the OTA dex optimizer. + PackageDexOptimizer optimizer = new OTADexoptPackageDexOptimizer( + collectingInstaller, mPackageManagerService.mInstallLock, mContext); + optimizer.performDexOpt(nextPackage, nextPackage.usesLibraryFiles, + null /* ISAs */, false /* checkProfiles */, + getCompilerFilterForReason(PackageManagerService.REASON_AB_OTA)); + + mCommandsForCurrentPackage = collectingConnection.commands; + if (mCommandsForCurrentPackage.isEmpty()) { + mCommandsForCurrentPackage = null; + } + + return true; } @Override @@ -152,8 +257,10 @@ public class OtaDexoptService extends IOtaDexopt.Stub { return; } - mPackageDexOptimizer.performDexOpt(nextPackage, nextPackage.usesLibraryFiles, - null /* ISAs */, false /* checkProfiles */, + PackageDexOptimizer optimizer = new OTADexoptPackageDexOptimizer( + mPackageManagerService.mInstaller, mPackageManagerService.mInstallLock, mContext); + optimizer.performDexOpt(nextPackage, nextPackage.usesLibraryFiles, null /* ISAs */, + false /* checkProfiles */, getCompilerFilterForReason(PackageManagerService.REASON_AB_OTA)); } @@ -218,4 +325,40 @@ public class OtaDexoptService extends IOtaDexopt.Stub { } } + + private static class RecordingInstallerConnection extends InstallerConnection { + public List<String> commands = new ArrayList<String>(1); + + @Override + public void setWarnIfHeld(Object warnIfHeld) { + throw new IllegalStateException("Should not reach here"); + } + + @Override + public synchronized String transact(String cmd) { + commands.add(cmd); + return "0"; + } + + @Override + public boolean mergeProfiles(int uid, String pkgName) throws InstallerException { + throw new IllegalStateException("Should not reach here"); + } + + @Override + public boolean dumpProfiles(String gid, String packageName, String codePaths) + throws InstallerException { + throw new IllegalStateException("Should not reach here"); + } + + @Override + public void disconnect() { + throw new IllegalStateException("Should not reach here"); + } + + @Override + public void waitForConnection() { + throw new IllegalStateException("Should not reach here"); + } + } } diff --git a/services/core/java/com/android/server/pm/OtaDexoptShellCommand.java b/services/core/java/com/android/server/pm/OtaDexoptShellCommand.java index e8fdfa50a12d..bbd404879a21 100644 --- a/services/core/java/com/android/server/pm/OtaDexoptShellCommand.java +++ b/services/core/java/com/android/server/pm/OtaDexoptShellCommand.java @@ -46,6 +46,8 @@ class OtaDexoptShellCommand extends ShellCommand { return runOtaDone(); case "step": return runOtaStep(); + case "next": + return runOtaNext(); case "progress": return runOtaProgress(); default: @@ -83,6 +85,11 @@ class OtaDexoptShellCommand extends ShellCommand { return 0; } + private int runOtaNext() throws RemoteException { + getOutPrintWriter().println(mInterface.nextDexoptCommand()); + return 0; + } + private int runOtaProgress() throws RemoteException { final float progress = mInterface.getProgress(); final PrintWriter pw = getOutPrintWriter(); @@ -103,6 +110,8 @@ class OtaDexoptShellCommand extends ShellCommand { pw.println(" Replies whether the OTA is complete or not."); pw.println(" step"); pw.println(" OTA dexopt the next package."); + pw.println(" next"); + pw.println(" Get parameters for OTA dexopt of the next package."); pw.println(" cleanup"); pw.println(" Clean up internal states. Ends an OTA session."); } diff --git a/services/retaildemo/java/com/android/server/retaildemo/RetailDemoModeService.java b/services/retaildemo/java/com/android/server/retaildemo/RetailDemoModeService.java index 96e6da741a82..51b1e38237a1 100644 --- a/services/retaildemo/java/com/android/server/retaildemo/RetailDemoModeService.java +++ b/services/retaildemo/java/com/android/server/retaildemo/RetailDemoModeService.java @@ -55,6 +55,8 @@ import android.provider.Settings; import android.util.Slog; import com.android.internal.os.BackgroundThread; import com.android.internal.R; +import com.android.internal.annotations.GuardedBy; +import com.android.internal.logging.MetricsLogger; import com.android.internal.widget.LockPatternUtils; import com.android.server.LocalServices; import com.android.server.ServiceThread; @@ -86,8 +88,12 @@ public class RetailDemoModeService extends SystemService { AudioSystem.STREAM_MUSIC }; + // Tron Vars + private static final String DEMO_SESSION_COUNT = "retail_demo_session_count"; + private static final String DEMO_SESSION_DURATION = "retail_demo_session_duration"; + boolean mDeviceInDemoMode = false; - int mCurrentUserId; + int mCurrentUserId = UserHandle.USER_SYSTEM; private ActivityManagerService mAms; private ActivityManagerInternal mAmi; private AudioManager mAudioManager; @@ -102,6 +108,15 @@ public class RetailDemoModeService extends SystemService { private String[] mCameraIdsWithFlash; private Configuration mPrimaryUserConfiguration; + final Object mActivityLock = new Object(); + // Whether the newly created demo user has interacted with the screen yet + @GuardedBy("mActivityLock") + boolean mUserUntouched; + @GuardedBy("mActivityLock") + long mFirstUserActivityTime; + @GuardedBy("mActivityLock") + long mLastUserActivityTime; + private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { @@ -136,18 +151,7 @@ public class RetailDemoModeService extends SystemService { mWakeLock.acquire(); break; case MSG_INACTIVITY_TIME_OUT: - final IPackageManager pm = AppGlobals.getPackageManager(); - int enabledState = PackageManager.COMPONENT_ENABLED_STATE_DEFAULT; - String demoLauncherComponent = getContext().getResources() - .getString(R.string.config_demoModeLauncherComponent); - try { - enabledState = pm.getComponentEnabledSetting( - ComponentName.unflattenFromString(demoLauncherComponent), - mCurrentUserId); - } catch (RemoteException exc) { - Slog.e(TAG, "Unable to talk to Package Manager", exc); - } - if (enabledState == PackageManager.COMPONENT_ENABLED_STATE_DISABLED) { + if (isDemoLauncherDisabled()) { Slog.i(TAG, "User inactivity timeout reached"); showInactivityCountdownDialog(); } @@ -158,6 +162,9 @@ public class RetailDemoModeService extends SystemService { } removeMessages(MSG_START_NEW_SESSION); removeMessages(MSG_INACTIVITY_TIME_OUT); + if (mCurrentUserId != UserHandle.USER_SYSTEM) { + logSessionDuration(); + } final UserInfo demoUser = getUserManager().createUser(DEMO_USER_NAME, UserInfo.FLAG_DEMO | UserInfo.FLAG_EPHEMERAL); if (demoUser != null) { @@ -190,6 +197,9 @@ public class RetailDemoModeService extends SystemService { public RetailDemoModeService(Context context) { super(context); + synchronized (mActivityLock) { + mFirstUserActivityTime = mLastUserActivityTime = SystemClock.uptimeMillis(); + } } private Notification createResetNotification() { @@ -213,6 +223,21 @@ public class RetailDemoModeService extends SystemService { return mResetDemoPendingIntent; } + boolean isDemoLauncherDisabled() { + IPackageManager pm = AppGlobals.getPackageManager(); + int enabledState = PackageManager.COMPONENT_ENABLED_STATE_DEFAULT; + String demoLauncherComponent = getContext().getResources() + .getString(R.string.config_demoModeLauncherComponent); + try { + enabledState = pm.getComponentEnabledSetting( + ComponentName.unflattenFromString(demoLauncherComponent), + mCurrentUserId); + } catch (RemoteException exc) { + Slog.e(TAG, "Unable to talk to Package Manager", exc); + } + return enabledState == PackageManager.COMPONENT_ENABLED_STATE_DISABLED; + } + private void setupDemoUser(UserInfo userInfo) { UserManager um = getUserManager(); UserHandle user = UserHandle.of(userInfo.id); @@ -226,6 +251,14 @@ public class RetailDemoModeService extends SystemService { Settings.Secure.SKIP_FIRST_USE_HINTS, 1, userInfo.id); } + void logSessionDuration() { + final int sessionDuration; + synchronized (mActivityLock) { + sessionDuration = (int) ((mLastUserActivityTime - mFirstUserActivityTime) / 1000); + } + MetricsLogger.count(getContext(), DEMO_SESSION_DURATION, sessionDuration); + } + private ActivityManagerService getActivityManager() { if (mAms == null) { mAms = (ActivityManagerService) ActivityManagerNative.getDefault(); @@ -395,11 +428,15 @@ public class RetailDemoModeService extends SystemService { turnOffAllFlashLights(); muteVolumeStreams(); mAmi.updatePersistentConfigurationForUser(getPrimaryUsersConfiguration(), userId); + synchronized (mActivityLock) { + mUserUntouched = true; + } + MetricsLogger.count(getContext(), DEMO_SESSION_COUNT, 1); + mHandler.removeMessages(MSG_INACTIVITY_TIME_OUT); } private RetailDemoModeServiceInternal mLocalService = new RetailDemoModeServiceInternal() { private static final long USER_ACTIVITY_DEBOUNCE_TIME = 2000; - private long mLastUserActivityTime = 0; @Override public void onUserActivity() { @@ -407,10 +444,17 @@ public class RetailDemoModeService extends SystemService { return; } long timeOfActivity = SystemClock.uptimeMillis(); - if (timeOfActivity < mLastUserActivityTime + USER_ACTIVITY_DEBOUNCE_TIME) { - return; + synchronized (mActivityLock) { + if (timeOfActivity < mLastUserActivityTime + USER_ACTIVITY_DEBOUNCE_TIME) { + return; + } + mLastUserActivityTime = timeOfActivity; + if (mUserUntouched && isDemoLauncherDisabled()) { + Slog.d(TAG, "retail_demo first touch"); + mUserUntouched = false; + mFirstUserActivityTime = timeOfActivity; + } } - mLastUserActivityTime = timeOfActivity; mHandler.removeMessages(MSG_INACTIVITY_TIME_OUT); mHandler.sendEmptyMessageDelayed(MSG_INACTIVITY_TIME_OUT, USER_INACTIVITY_TIMEOUT); } diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java index 39302081b79a..2a0a9a640c6d 100644 --- a/telecomm/java/android/telecom/Connection.java +++ b/telecomm/java/android/telecom/Connection.java @@ -586,6 +586,8 @@ public abstract class Connection extends Conferenceable { public void onExtrasChanged(Connection c, Bundle extras) {} public void onExtrasRemoved(Connection c, List<String> keys) {} public void onConnectionEvent(Connection c, String event, Bundle extras) {} + /** @hide */ + public void onConferenceSupportedChanged(Connection c, boolean isConferenceSupported) {} } /** @@ -2355,6 +2357,19 @@ public abstract class Connection extends Conferenceable { } /** + * Notifies listeners when a change has occurred to the Connection which impacts its ability to + * be a part of a conference call. + * @param isConferenceSupported {@code true} if the connection supports being part of a + * conference call, {@code false} otherwise. + * @hide + */ + protected void notifyConferenceSupportedChanged(boolean isConferenceSupported) { + for (Listener l : mListeners) { + l.onConferenceSupportedChanged(this, isConferenceSupported); + } + } + + /** * Sends an event associated with this {@code Connection}, with associated event extras. * * Events are exposed to {@link InCallService} implementations via the diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index 2458cc8c85ae..8399bb669da0 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -808,6 +808,16 @@ public class CarrierConfigManager { public static final String KEY_DROP_VIDEO_CALL_WHEN_ANSWERING_AUDIO_CALL_BOOL = "drop_video_call_when_answering_audio_call_bool"; + /** + * Flag indicating whether the carrier supports merging wifi calls when VoWIFI is disabled. + * This can happen in the case of a carrier which allows offloading video calls to WIFI + * separately of whether voice over wifi is enabled. In such a scenario when two video calls + * are downgraded to voice, they remain over wifi. However, if VoWIFI is disabled, these calls + * cannot be merged. + */ + public static final String KEY_ALLOW_MERGE_WIFI_CALLS_WHEN_VOWIFI_OFF_BOOL = + "allow_merge_wifi_calls_when_vowifi_off_bool"; + /** The default value for every variable. */ private final static PersistableBundle sDefaults; @@ -959,8 +969,9 @@ public class CarrierConfigManager { // Order is important - lowest precidence first sDefaults.putStringArray(KEY_RATCHET_RAT_FAMILIES, new String[]{"1,2","7,8,12","3,11,9,10,15","14,19"}); - sDefaults.putBoolean(KEY_TREAT_DOWNGRADED_VIDEO_CALLS_AS_VIDEO_CALLS_BOOL, true); - sDefaults.putBoolean(KEY_DROP_VIDEO_CALL_WHEN_ANSWERING_AUDIO_CALL_BOOL, true); + sDefaults.putBoolean(KEY_TREAT_DOWNGRADED_VIDEO_CALLS_AS_VIDEO_CALLS_BOOL, false); + sDefaults.putBoolean(KEY_DROP_VIDEO_CALL_WHEN_ANSWERING_AUDIO_CALL_BOOL, false); + sDefaults.putBoolean(KEY_ALLOW_MERGE_WIFI_CALLS_WHEN_VOWIFI_OFF_BOOL, true); } /** |