Merge "Use the briefcase icon with the circle" into pi-dev
diff --git a/api/test-current.txt b/api/test-current.txt
index f7bfeae..5cffd0e 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -917,6 +917,7 @@
public class TelephonyManager {
method public int getCarrierIdListVersion();
+ method public void refreshUiccProfile();
method public void setCarrierTestOverride(java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String, java.lang.String);
field public static final int UNKNOWN_CARRIER_ID_LIST_VERSION = -1; // 0xffffffff
}
diff --git a/config/hiddenapi-light-greylist.txt b/config/hiddenapi-light-greylist.txt
index 19ce180..6228473 100644
--- a/config/hiddenapi-light-greylist.txt
+++ b/config/hiddenapi-light-greylist.txt
@@ -229,6 +229,7 @@
Landroid/app/AppOpsManager;->OP_WRITE_CONTACTS:I
Landroid/app/AppOpsManager;->OP_WRITE_SMS:I
Landroid/app/AppOpsManager;->permissionToOpCode(Ljava/lang/String;)I
+Landroid/app/AppOpsManager;->setRestriction(III[Ljava/lang/String;)V
Landroid/app/AppOpsManager;->sOpPerms:[Ljava/lang/String;
Landroid/app/AppOpsManager;->strOpToOp(Ljava/lang/String;)I
Landroid/app/backup/BackupDataInput$EntityHeader;->dataSize:I
@@ -420,6 +421,7 @@
Landroid/app/PendingIntent;->getActivityAsUser(Landroid/content/Context;ILandroid/content/Intent;ILandroid/os/Bundle;Landroid/os/UserHandle;)Landroid/app/PendingIntent;
Landroid/app/PendingIntent;->getIntent()Landroid/content/Intent;
Landroid/app/PendingIntent;->isActivity()Z
+Landroid/app/PictureInPictureParams;->getAspectRatio()F
Landroid/app/Presentation;->createPresentationContext(Landroid/content/Context;Landroid/view/Display;I)Landroid/content/Context;
Landroid/app/ProgressDialog;->mProgressNumber:Landroid/widget/TextView;
Landroid/app/QueuedWork;->addFinisher(Ljava/lang/Runnable;)V
@@ -488,6 +490,7 @@
Landroid/bluetooth/BluetoothA2dp;->ACTION_CODEC_CONFIG_CHANGED:Ljava/lang/String;
Landroid/bluetooth/BluetoothA2dp;->connect(Landroid/bluetooth/BluetoothDevice;)Z
Landroid/bluetooth/BluetoothA2dp;->disableOptionalCodecs(Landroid/bluetooth/BluetoothDevice;)V
+Landroid/bluetooth/BluetoothA2dp;->disconnect(Landroid/bluetooth/BluetoothDevice;)Z
Landroid/bluetooth/BluetoothA2dp;->enableOptionalCodecs(Landroid/bluetooth/BluetoothDevice;)V
Landroid/bluetooth/BluetoothA2dp;->getActiveDevice()Landroid/bluetooth/BluetoothDevice;
Landroid/bluetooth/BluetoothA2dp;->getCodecStatus(Landroid/bluetooth/BluetoothDevice;)Landroid/bluetooth/BluetoothCodecStatus;
@@ -695,6 +698,7 @@
Landroid/content/pm/IPackageDataObserver$Stub$Proxy;->mRemote:Landroid/os/IBinder;
Landroid/content/pm/IPackageDataObserver$Stub;->asInterface(Landroid/os/IBinder;)Landroid/content/pm/IPackageDataObserver;
Landroid/content/pm/IPackageDataObserver;->onRemoveCompleted(Ljava/lang/String;Z)V
+Landroid/content/pm/IPackageDeleteObserver$Stub;->asInterface(Landroid/os/IBinder;)Landroid/content/pm/IPackageDeleteObserver;
Landroid/content/pm/IPackageDeleteObserver2$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
Landroid/content/pm/IPackageDeleteObserver2$Stub$Proxy;->mRemote:Landroid/os/IBinder;
Landroid/content/pm/IPackageDeleteObserver2;->onPackageDeleted(Ljava/lang/String;ILjava/lang/String;)V
@@ -738,6 +742,7 @@
Landroid/content/pm/IPackageStatsObserver$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
Landroid/content/pm/IPackageStatsObserver$Stub$Proxy;->mRemote:Landroid/os/IBinder;
Landroid/content/pm/IPackageStatsObserver$Stub;-><init>()V
+Landroid/content/pm/IPackageStatsObserver$Stub;->asInterface(Landroid/os/IBinder;)Landroid/content/pm/IPackageStatsObserver;
Landroid/content/pm/IShortcutService$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
Landroid/content/pm/IShortcutService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/content/pm/IShortcutService;
Landroid/content/pm/LauncherActivityInfo;->mActivityInfo:Landroid/content/pm/ActivityInfo;
@@ -796,6 +801,7 @@
Landroid/content/pm/PackageParser$Package;->configPreferences:Ljava/util/ArrayList;
Landroid/content/pm/PackageParser$Package;->instrumentation:Ljava/util/ArrayList;
Landroid/content/pm/PackageParser$Package;->mAppMetaData:Landroid/os/Bundle;
+Landroid/content/pm/PackageParser$Package;->mKeySetMapping:Landroid/util/ArrayMap;
Landroid/content/pm/PackageParser$Package;->mPreferredOrder:I
Landroid/content/pm/PackageParser$Package;->mSharedUserId:Ljava/lang/String;
Landroid/content/pm/PackageParser$Package;->mSharedUserLabel:I
@@ -891,6 +897,7 @@
Landroid/content/res/Resources;->setCompatibilityInfo(Landroid/content/res/CompatibilityInfo;)V
Landroid/content/res/Resources;->updateSystemConfiguration(Landroid/content/res/Configuration;Landroid/util/DisplayMetrics;Landroid/content/res/CompatibilityInfo;)V
Landroid/content/res/ResourcesImpl;->getAssets()Landroid/content/res/AssetManager;
+Landroid/content/res/ResourcesImpl;->getValue(ILandroid/util/TypedValue;Z)V
Landroid/content/res/ResourcesImpl;->mAccessLock:Ljava/lang/Object;
Landroid/content/res/ResourcesImpl;->mAnimatorCache:Landroid/content/res/ConfigurationBoundResourceCache;
Landroid/content/res/ResourcesImpl;->mAssets:Landroid/content/res/AssetManager;
@@ -1081,7 +1088,9 @@
Landroid/graphics/GraphicBuffer;->mNativeObject:J
Landroid/graphics/ImageDecoder;->postProcessAndRelease(Landroid/graphics/Canvas;)I
Landroid/graphics/Insets;->left:I
+Landroid/graphics/Insets;->top:I
Landroid/graphics/Insets;->right:I
+Landroid/graphics/Insets;->bottom:I
Landroid/graphics/LinearGradient;->mColors:[I
Landroid/graphics/Matrix;->native_instance:J
Landroid/graphics/Movie;-><init>(J)V
@@ -1264,6 +1273,7 @@
Landroid/icu/text/SpoofChecker$ScriptSet;->setAll()V
Landroid/icu/text/TimeZoneNames$DefaultTimeZoneNames$FactoryImpl;-><init>()V
Landroid/icu/text/Transliterator;->createFromRules(Ljava/lang/String;Ljava/lang/String;I)Landroid/icu/text/Transliterator;
+Landroid/icu/text/Transliterator;->getInstance(Ljava/lang/String;)Landroid/icu/text/Transliterator;
Landroid/icu/text/Transliterator;->transliterate(Ljava/lang/String;)Ljava/lang/String;
Landroid/icu/text/UFormat;->getLocale(Landroid/icu/util/ULocale$Type;)Landroid/icu/util/ULocale;
Landroid/icu/util/Calendar;->getLocale(Landroid/icu/util/ULocale$Type;)Landroid/icu/util/ULocale;
@@ -2096,6 +2106,7 @@
Landroid/R$styleable;->CheckedTextView_checkMark:I
Landroid/R$styleable;->CompoundButton:[I
Landroid/R$styleable;->CompoundButton_button:I
+Landroid/R$styleable;->DrawableStates:[I
Landroid/R$styleable;->ImageView:[I
Landroid/R$styleable;->ImageView_adjustViewBounds:I
Landroid/R$styleable;->ImageView_baselineAlignBottom:I
@@ -2358,6 +2369,7 @@
Landroid/telephony/CellSignalStrengthLte;->mRsrq:I
Landroid/telephony/CellSignalStrengthLte;->mRssnr:I
Landroid/telephony/CellSignalStrengthLte;->mSignalStrength:I
+Landroid/telephony/CellSignalStrengthLte;->mTimingAdvance:I
Landroid/telephony/CellSignalStrengthWcdma;->mBitErrorRate:I
Landroid/telephony/CellSignalStrengthWcdma;->mSignalStrength:I
Landroid/telephony/PhoneNumberUtils;->isLocalEmergencyNumber(Landroid/content/Context;ILjava/lang/String;)Z
@@ -2367,6 +2379,7 @@
Landroid/telephony/SignalStrength;->getAsuLevel()I
Landroid/telephony/SignalStrength;->getCdmaLevel()I
Landroid/telephony/SignalStrength;->getDbm()I
+Landroid/telephony/SignalStrength;->getGsmDbm()I
Landroid/telephony/SignalStrength;->getLteDbm()I
Landroid/telephony/SignalStrength;->getLteRsrp()I
Landroid/telephony/SignalStrength;->getLteRsrq()I
@@ -2431,6 +2444,7 @@
Landroid/telephony/TelephonyManager;->hasIccCard(I)Z
Landroid/telephony/TelephonyManager;->isMultiSimEnabled()Z
Landroid/telephony/TelephonyManager;->isNetworkRoaming(I)Z
+Landroid/telephony/TelephonyManager;->isVideoTelephonyAvailable()Z
Landroid/telephony/TelephonyManager;->isVolteAvailable()Z
Landroid/telephony/TelephonyManager;->isWifiCallingAvailable()Z
Landroid/telephony/TelephonyManager;->mSubscriptionManager:Landroid/telephony/SubscriptionManager;
@@ -2609,6 +2623,7 @@
Landroid/view/inputmethod/InputMethodManager;->finishInputLocked()V
Landroid/view/inputmethod/InputMethodManager;->focusIn(Landroid/view/View;)V
Landroid/view/inputmethod/InputMethodManager;->focusOut(Landroid/view/View;)V
+Landroid/view/inputmethod/InputMethodManager;->getClient()Lcom/android/internal/view/IInputMethodClient;
Landroid/view/inputmethod/InputMethodManager;->getInputMethodWindowVisibleHeight()I
Landroid/view/inputmethod/InputMethodManager;->getInstance()Landroid/view/inputmethod/InputMethodManager;
Landroid/view/inputmethod/InputMethodManager;->mCurId:Ljava/lang/String;
@@ -2785,6 +2800,7 @@
Landroid/view/View;->includeForAccessibility()Z
Landroid/view/View;->initializeScrollbars(Landroid/content/res/TypedArray;)V
Landroid/view/View;->internalSetPadding(IIII)V
+Landroid/view/View;->invalidateParentIfNeeded()V
Landroid/view/View;->isPaddingResolved()Z
Landroid/view/View;->isRootNamespace()Z
Landroid/view/View;->isVisibleToUser()Z
@@ -3069,6 +3085,7 @@
Landroid/widget/ImageView;->mDrawMatrix:Landroid/graphics/Matrix;
Landroid/widget/ImageView;->mMaxHeight:I
Landroid/widget/ImageView;->mMaxWidth:I
+Landroid/widget/ImageView;->mRecycleableBitmapDrawable:Landroid/graphics/drawable/BitmapDrawable;
Landroid/widget/ImageView;->mResource:I
Landroid/widget/ImageView;->mUri:Landroid/net/Uri;
Landroid/widget/ImageView;->updateDrawable(Landroid/graphics/drawable/Drawable;)V
@@ -3573,11 +3590,13 @@
Lcom/android/internal/telephony/ITelephony;->disableDataConnectivity()Z
Lcom/android/internal/telephony/ITelephony;->enableDataConnectivity()Z
Lcom/android/internal/telephony/ITelephony;->endCall()Z
+Lcom/android/internal/telephony/ITelephony;->endCallForSubscriber(I)Z
Lcom/android/internal/telephony/ITelephony;->getCallState()I
Lcom/android/internal/telephony/ITelephony;->getDataState()I
Lcom/android/internal/telephony/ITelephony;->isIdle(Ljava/lang/String;)Z
Lcom/android/internal/telephony/ITelephony;->setRadio(Z)Z
Lcom/android/internal/telephony/ITelephony;->silenceRinger()V
+Lcom/android/internal/telephony/ITelephonyRegistry$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
Lcom/android/internal/telephony/ITelephonyRegistry$Stub;->asInterface(Landroid/os/IBinder;)Lcom/android/internal/telephony/ITelephonyRegistry;
Lcom/android/internal/telephony/ITelephonyRegistry;->notifyCallState(ILjava/lang/String;)V
Lcom/android/internal/telephony/SmsHeader$ConcatRef;->msgCount:I
@@ -3714,6 +3733,7 @@
Ldalvik/system/VMDebug;->isDebuggerConnected()Z
Ldalvik/system/VMRuntime;->addressOf(Ljava/lang/Object;)J
Ldalvik/system/VMRuntime;->clearGrowthLimit()V
+Ldalvik/system/VMRuntime;->gcSoftReferences()V
Ldalvik/system/VMRuntime;->getCurrentInstructionSet()Ljava/lang/String;
Ldalvik/system/VMRuntime;->getExternalBytesAllocated()J
Ldalvik/system/VMRuntime;->getInstructionSet(Ljava/lang/String;)Ljava/lang/String;
diff --git a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
index 452225c..ed684d7 100644
--- a/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
+++ b/core/java/android/accessibilityservice/AccessibilityServiceInfo.java
@@ -302,7 +302,16 @@
/**
* This flag requests that all fingerprint gestures be sent to the accessibility service.
- * It is handled in {@link FingerprintGestureController}
+ * <p>
+ * Services that want to set this flag have to declare the capability
+ * to retrieve window content in their meta-data by setting the attribute
+ * {@link android.R.attr#canRequestFingerprintGestures} to
+ * true, otherwise this flag will be ignored. For how to declare the meta-data
+ * of a service refer to {@value AccessibilityService#SERVICE_META_DATA}.
+ * </p>
+ *
+ * @see android.R.styleable#AccessibilityService_canRequestFingerprintGestures
+ * @see AccessibilityService#getFingerprintGestureController()
*/
public static final int FLAG_REQUEST_FINGERPRINT_GESTURES = 0x00000200;
diff --git a/core/java/android/app/AlertDialog.java b/core/java/android/app/AlertDialog.java
index a44bd03..07b4b9c 100644
--- a/core/java/android/app/AlertDialog.java
+++ b/core/java/android/app/AlertDialog.java
@@ -16,8 +16,6 @@
package android.app;
-import com.android.internal.app.AlertController;
-
import android.annotation.ArrayRes;
import android.annotation.AttrRes;
import android.annotation.DrawableRes;
@@ -30,17 +28,19 @@
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Message;
+import android.text.Layout;
+import android.text.method.MovementMethod;
import android.util.TypedValue;
import android.view.ContextThemeWrapper;
import android.view.KeyEvent;
import android.view.View;
-import android.view.WindowManager;
import android.widget.AdapterView;
import android.widget.Button;
import android.widget.ListAdapter;
import android.widget.ListView;
import com.android.internal.R;
+import com.android.internal.app.AlertController;
/**
* A subclass of Dialog that can display one, two or three buttons. If you only want to
@@ -54,7 +54,7 @@
* </pre>
*
* <p>The AlertDialog class takes care of automatically setting
- * {@link WindowManager.LayoutParams#FLAG_ALT_FOCUSABLE_IM
+ * {@link android.view.WindowManager.LayoutParams#FLAG_ALT_FOCUSABLE_IM
* WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM} for you based on whether
* any views in the dialog return true from {@link View#onCheckIsTextEditor()
* View.onCheckIsTextEditor()}. Generally you want this set for a Dialog
@@ -266,6 +266,17 @@
mAlert.setMessage(message);
}
+ /** @hide */
+ public void setMessageMovementMethod(MovementMethod movementMethod) {
+ mAlert.setMessageMovementMethod(movementMethod);
+ }
+
+ /** @hide */
+ public void setMessageHyphenationFrequency(
+ @Layout.HyphenationFrequency int hyphenationFrequency) {
+ mAlert.setMessageHyphenationFrequency(hyphenationFrequency);
+ }
+
/**
* Set the view to display in that dialog.
*/
diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index ab0001c..dcf4eec 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -109,6 +109,48 @@
*/
public static final int MODE_DEFAULT = 3;
+ /**
+ * Metrics about an op when its uid is persistent.
+ * @hide
+ */
+ public static final int UID_STATE_PERSISTENT = 0;
+
+ /**
+ * Metrics about an op when its uid is at the top.
+ * @hide
+ */
+ public static final int UID_STATE_TOP = 1;
+
+ /**
+ * Metrics about an op when its uid is running a foreground service.
+ * @hide
+ */
+ public static final int UID_STATE_FOREGROUND_SERVICE = 2;
+
+ /**
+ * Metrics about an op when its uid is in the foreground for any other reasons.
+ * @hide
+ */
+ public static final int UID_STATE_FOREGROUND = 3;
+
+ /**
+ * Metrics about an op when its uid is in the background for any reason.
+ * @hide
+ */
+ public static final int UID_STATE_BACKGROUND = 4;
+
+ /**
+ * Metrics about an op when its uid is cached.
+ * @hide
+ */
+ public static final int UID_STATE_CACHED = 5;
+
+ /**
+ * Number of uid states we track.
+ * @hide
+ */
+ public static final int _NUM_UID_STATE = 6;
+
// when adding one of these:
// - increment _NUM_OP
// - define an OPSTR_* constant (marked as @SystemApi)
@@ -1471,8 +1513,8 @@
public static class OpEntry implements Parcelable {
private final int mOp;
private final int mMode;
- private final long mTime;
- private final long mRejectTime;
+ private final long[] mTimes;
+ private final long[] mRejectTimes;
private final int mDuration;
private final int mProxyUid;
private final String mProxyPackageName;
@@ -1481,8 +1523,23 @@
int proxyUid, String proxyPackage) {
mOp = op;
mMode = mode;
- mTime = time;
- mRejectTime = rejectTime;
+ mTimes = new long[_NUM_UID_STATE];
+ mRejectTimes = new long[_NUM_UID_STATE];
+ mTimes[0] = time;
+ mRejectTimes[0] = rejectTime;
+ mDuration = duration;
+ mProxyUid = proxyUid;
+ mProxyPackageName = proxyPackage;
+ }
+
+ public OpEntry(int op, int mode, long[] times, long[] rejectTimes, int duration,
+ int proxyUid, String proxyPackage) {
+ mOp = op;
+ mMode = mode;
+ mTimes = new long[_NUM_UID_STATE];
+ mRejectTimes = new long[_NUM_UID_STATE];
+ System.arraycopy(times, 0, mTimes, 0, _NUM_UID_STATE);
+ System.arraycopy(rejectTimes, 0, mRejectTimes, 0, _NUM_UID_STATE);
mDuration = duration;
mProxyUid = proxyUid;
mProxyPackageName = proxyPackage;
@@ -1497,11 +1554,31 @@
}
public long getTime() {
- return mTime;
+ long time = 0;
+ for (int i = 0; i < _NUM_UID_STATE; i++) {
+ if (mTimes[i] > time) {
+ time = mTimes[i];
+ }
+ }
+ return time;
+ }
+
+ public long getTimeFor(int uidState) {
+ return mTimes[uidState];
}
public long getRejectTime() {
- return mRejectTime;
+ long time = 0;
+ for (int i = 0; i < _NUM_UID_STATE; i++) {
+ if (mRejectTimes[i] > time) {
+ time = mRejectTimes[i];
+ }
+ }
+ return time;
+ }
+
+ public long getRejectTimeFor(int uidState) {
+ return mRejectTimes[uidState];
}
public boolean isRunning() {
@@ -1509,7 +1586,7 @@
}
public int getDuration() {
- return mDuration == -1 ? (int)(System.currentTimeMillis()-mTime) : mDuration;
+ return mDuration;
}
public int getProxyUid() {
@@ -1529,8 +1606,8 @@
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(mOp);
dest.writeInt(mMode);
- dest.writeLong(mTime);
- dest.writeLong(mRejectTime);
+ dest.writeLongArray(mTimes);
+ dest.writeLongArray(mRejectTimes);
dest.writeInt(mDuration);
dest.writeInt(mProxyUid);
dest.writeString(mProxyPackageName);
@@ -1539,8 +1616,8 @@
OpEntry(Parcel source) {
mOp = source.readInt();
mMode = source.readInt();
- mTime = source.readLong();
- mRejectTime = source.readLong();
+ mTimes = source.createLongArray();
+ mRejectTimes = source.createLongArray();
mDuration = source.readInt();
mProxyUid = source.readInt();
mProxyPackageName = source.readString();
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index eeec7ca..c5b8019 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -5553,7 +5553,8 @@
*
* @hide
*/
- public static Notification maybeCloneStrippedForDelivery(Notification n, boolean isLowRam) {
+ public static Notification maybeCloneStrippedForDelivery(Notification n, boolean isLowRam,
+ Context context) {
String templateClass = n.extras.getString(EXTRA_TEMPLATE);
// Only strip views for known Styles because we won't know how to
@@ -5595,9 +5596,13 @@
clone.extras.remove(EXTRA_REBUILD_HEADS_UP_CONTENT_VIEW_ACTION_COUNT);
}
if (isLowRam) {
- clone.extras.remove(Notification.TvExtender.EXTRA_TV_EXTENDER);
- clone.extras.remove(WearableExtender.EXTRA_WEARABLE_EXTENSIONS);
- clone.extras.remove(CarExtender.EXTRA_CAR_EXTENDER);
+ String[] allowedServices = context.getResources().getStringArray(
+ R.array.config_allowedManagedServicesOnLowRamDevices);
+ if (allowedServices.length == 0) {
+ clone.extras.remove(Notification.TvExtender.EXTRA_TV_EXTENDER);
+ clone.extras.remove(WearableExtender.EXTRA_WEARABLE_EXTENSIONS);
+ clone.extras.remove(CarExtender.EXTRA_CAR_EXTENDER);
+ }
}
return clone;
}
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index 93be932..f6dc5d1 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -399,7 +399,8 @@
ActivityManager am = (ActivityManager) mContext.getSystemService(Context.ACTIVITY_SERVICE);
boolean isLowRam = am.isLowRamDevice();
- final Notification copy = Builder.maybeCloneStrippedForDelivery(notification, isLowRam);
+ final Notification copy = Builder.maybeCloneStrippedForDelivery(notification, isLowRam,
+ mContext);
try {
service.enqueueNotificationWithTag(pkg, mContext.getOpPackageName(), tag, id,
copy, user.getIdentifier());
diff --git a/core/java/android/app/usage/EventList.java b/core/java/android/app/usage/EventList.java
new file mode 100644
index 0000000..aaae57e5
--- /dev/null
+++ b/core/java/android/app/usage/EventList.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.usage;
+
+import java.util.ArrayList;
+
+/**
+ * A container to keep {@link UsageEvents.Event usage events} in non-descending order of their
+ * {@link UsageEvents.Event#mTimeStamp timestamps}.
+ *
+ * @hide
+ */
+public class EventList {
+
+ private final ArrayList<UsageEvents.Event> mEvents;
+
+ /**
+ * Create a new event list with default capacity
+ */
+ public EventList() {
+ mEvents = new ArrayList<>();
+ }
+
+ /**
+ * Returns the size of the list
+ * @return the number of events in the list
+ */
+ public int size() {
+ return mEvents.size();
+ }
+
+ /**
+ * Removes all events from the list
+ */
+ public void clear() {
+ mEvents.clear();
+ }
+
+ /**
+ * Returns the {@link UsageEvents.Event event} at the specified position in this list.
+ * @param index the index of the event to return, such that {@code 0 <= index < size()}
+ * @return The {@link UsageEvents.Event event} at position {@code index}
+ */
+ public UsageEvents.Event get(int index) {
+ return mEvents.get(index);
+ }
+
+ /**
+ * Inserts the given {@link UsageEvents.Event event} into the list while keeping the list sorted
+ * based on the event {@link UsageEvents.Event#mTimeStamp timestamps}.
+ *
+ * @param event The event to insert
+ */
+ public void insert(UsageEvents.Event event) {
+ final int size = mEvents.size();
+ // fast case: just append if this is the latest event
+ if (size == 0 || event.mTimeStamp >= mEvents.get(size - 1).mTimeStamp) {
+ mEvents.add(event);
+ return;
+ }
+ // To minimize number of elements being shifted, insert at the first occurrence of the next
+ // greatest timestamp in the list.
+ final int insertIndex = firstIndexOnOrAfter(event.mTimeStamp + 1);
+ mEvents.add(insertIndex, event);
+ }
+
+ /**
+ * Finds the index of the first event whose timestamp is greater than or equal to the given
+ * timestamp.
+ *
+ * @param timeStamp The timestamp for which to search the list.
+ * @return The smallest {@code index} for which {@code (get(index).mTimeStamp >= timeStamp)} is
+ * {@code true}, or {@link #size() size} if no such {@code index} exists.
+ */
+ public int firstIndexOnOrAfter(long timeStamp) {
+ final int size = mEvents.size();
+ int result = size;
+ int lo = 0;
+ int hi = size - 1;
+ while (lo <= hi) {
+ final int mid = (lo + hi) >>> 1;
+ final long midTimeStamp = mEvents.get(mid).mTimeStamp;
+ if (midTimeStamp >= timeStamp) {
+ hi = mid - 1;
+ result = mid;
+ } else {
+ lo = mid + 1;
+ }
+ }
+ return result;
+ }
+}
diff --git a/core/java/android/app/usage/TimeSparseArray.java b/core/java/android/app/usage/TimeSparseArray.java
index 4ec0e9e..2bd6b24 100644
--- a/core/java/android/app/usage/TimeSparseArray.java
+++ b/core/java/android/app/usage/TimeSparseArray.java
@@ -27,14 +27,12 @@
public class TimeSparseArray<E> extends LongSparseArray<E> {
private static final String TAG = TimeSparseArray.class.getSimpleName();
+ private boolean mWtfReported;
+
public TimeSparseArray() {
super();
}
- public TimeSparseArray(int initialCapacity) {
- super(initialCapacity);
- }
-
/**
* Finds the index of the first element whose timestamp is greater or equal to
* the given time.
@@ -75,22 +73,16 @@
/**
* {@inheritDoc}
*
- * Overridden to ensure no collisions. The key (time in milliseconds) is incremented till an
- * empty place is found.
+ * <p> As this container is being used only to keep {@link android.util.AtomicFile files},
+ * there should not be any collisions. Reporting a {@link Slog#wtf(String, String)} in case that
+ * happens, as that will lead to one whole file being dropped.
*/
@Override
public void put(long key, E value) {
- final long origKey = key;
- int keyIndex = indexOfKey(key);
- if (keyIndex >= 0) {
- final long sz = size();
- while (keyIndex < sz && keyAt(keyIndex) == key) {
- key++;
- keyIndex++;
- }
- if (key >= origKey + 100) {
- Slog.w(TAG, "Value " + value + " supposed to be inserted at " + origKey
- + " displaced to " + key);
+ if (indexOfKey(key) >= 0) {
+ if (!mWtfReported) {
+ Slog.wtf(TAG, "Overwriting value " + get(key) + " by " + value);
+ mWtfReported = true;
}
}
super.put(key, value);
diff --git a/core/java/android/net/IpSecManager.java b/core/java/android/net/IpSecManager.java
index a61ea50..d4c3edc 100644
--- a/core/java/android/net/IpSecManager.java
+++ b/core/java/android/net/IpSecManager.java
@@ -26,6 +26,9 @@
import android.os.Binder;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
+import android.os.ServiceSpecificException;
+import android.system.ErrnoException;
+import android.system.OsConstants;
import android.util.AndroidException;
import android.util.Log;
@@ -172,11 +175,16 @@
public void close() {
try {
mService.releaseSecurityParameterIndex(mResourceId);
- mResourceId = INVALID_RESOURCE_ID;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
+ } catch (Exception e) {
+ // On close we swallow all random exceptions since failure to close is not
+ // actionable by the user.
+ Log.e(TAG, "Failed to close " + this + ", Exception=" + e);
+ } finally {
+ mResourceId = INVALID_RESOURCE_ID;
+ mCloseGuard.close();
}
- mCloseGuard.close();
}
/** Check that the SPI was closed properly. */
@@ -227,7 +235,6 @@
throw new RuntimeException(
"Invalid Resource ID returned by IpSecService: " + status);
}
-
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -239,6 +246,17 @@
public int getResourceId() {
return mResourceId;
}
+
+ @Override
+ public String toString() {
+ return new StringBuilder()
+ .append("SecurityParameterIndex{spi=")
+ .append(mSpi)
+ .append(",resourceId=")
+ .append(mResourceId)
+ .append("}")
+ .toString();
+ }
}
/**
@@ -261,7 +279,11 @@
mService,
destinationAddress,
IpSecManager.INVALID_SECURITY_PARAMETER_INDEX);
+ } catch (ServiceSpecificException e) {
+ throw rethrowUncheckedExceptionFromServiceSpecificException(e);
} catch (SpiUnavailableException unlikely) {
+ // Because this function allocates a totally random SPI, it really shouldn't ever
+ // fail to allocate an SPI; we simply need this because the exception is checked.
throw new ResourceUnavailableException("No SPIs available");
}
}
@@ -274,8 +296,8 @@
*
* @param destinationAddress the destination address for traffic bearing the requested SPI.
* For inbound traffic, the destination should be an address currently assigned on-device.
- * @param requestedSpi the requested SPI, or '0' to allocate a random SPI. The range 1-255 is
- * reserved and may not be used. See RFC 4303 Section 2.1.
+ * @param requestedSpi the requested SPI. The range 1-255 is reserved and may not be used. See
+ * RFC 4303 Section 2.1.
* @return the reserved SecurityParameterIndex
* @throws {@link #ResourceUnavailableException} indicating that too many SPIs are
* currently allocated for this user
@@ -289,7 +311,11 @@
if (requestedSpi == IpSecManager.INVALID_SECURITY_PARAMETER_INDEX) {
throw new IllegalArgumentException("Requested SPI must be a valid (non-zero) SPI");
}
- return new SecurityParameterIndex(mService, destinationAddress, requestedSpi);
+ try {
+ return new SecurityParameterIndex(mService, destinationAddress, requestedSpi);
+ } catch (ServiceSpecificException e) {
+ throw rethrowUncheckedExceptionFromServiceSpecificException(e);
+ }
}
/**
@@ -424,6 +450,8 @@
// constructor takes control and closes the user's FD when we exit the method.
try (ParcelFileDescriptor pfd = ParcelFileDescriptor.dup(socket)) {
mService.applyTransportModeTransform(pfd, direction, transform.getResourceId());
+ } catch (ServiceSpecificException e) {
+ throw rethrowCheckedExceptionFromServiceSpecificException(e);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -482,6 +510,8 @@
public void removeTransportModeTransforms(@NonNull FileDescriptor socket) throws IOException {
try (ParcelFileDescriptor pfd = ParcelFileDescriptor.dup(socket)) {
mService.removeTransportModeTransforms(pfd);
+ } catch (ServiceSpecificException e) {
+ throw rethrowCheckedExceptionFromServiceSpecificException(e);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -575,6 +605,13 @@
mResourceId = INVALID_RESOURCE_ID;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
+ } catch (Exception e) {
+ // On close we swallow all random exceptions since failure to close is not
+ // actionable by the user.
+ Log.e(TAG, "Failed to close " + this + ", Exception=" + e);
+ } finally {
+ mResourceId = INVALID_RESOURCE_ID;
+ mCloseGuard.close();
}
try {
@@ -583,7 +620,6 @@
Log.e(TAG, "Failed to close UDP Encapsulation Socket with Port= " + mPort);
throw e;
}
- mCloseGuard.close();
}
/** Check that the socket was closed properly. */
@@ -600,6 +636,17 @@
public int getResourceId() {
return mResourceId;
}
+
+ @Override
+ public String toString() {
+ return new StringBuilder()
+ .append("UdpEncapsulationSocket{port=")
+ .append(mPort)
+ .append(",resourceId=")
+ .append(mResourceId)
+ .append("}")
+ .toString();
+ }
};
/**
@@ -627,7 +674,11 @@
if (port == 0) {
throw new IllegalArgumentException("Specified port must be a valid port number!");
}
- return new UdpEncapsulationSocket(mService, port);
+ try {
+ return new UdpEncapsulationSocket(mService, port);
+ } catch (ServiceSpecificException e) {
+ throw rethrowCheckedExceptionFromServiceSpecificException(e);
+ }
}
/**
@@ -650,7 +701,11 @@
@NonNull
public UdpEncapsulationSocket openUdpEncapsulationSocket()
throws IOException, ResourceUnavailableException {
- return new UdpEncapsulationSocket(mService, 0);
+ try {
+ return new UdpEncapsulationSocket(mService, 0);
+ } catch (ServiceSpecificException e) {
+ throw rethrowCheckedExceptionFromServiceSpecificException(e);
+ }
}
/**
@@ -696,6 +751,8 @@
try {
mService.addAddressToTunnelInterface(
mResourceId, new LinkAddress(address, prefixLen), mOpPackageName);
+ } catch (ServiceSpecificException e) {
+ throw rethrowCheckedExceptionFromServiceSpecificException(e);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -715,6 +772,8 @@
try {
mService.removeAddressFromTunnelInterface(
mResourceId, new LinkAddress(address, prefixLen), mOpPackageName);
+ } catch (ServiceSpecificException e) {
+ throw rethrowCheckedExceptionFromServiceSpecificException(e);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -767,11 +826,16 @@
public void close() {
try {
mService.deleteTunnelInterface(mResourceId, mOpPackageName);
- mResourceId = INVALID_RESOURCE_ID;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
+ } catch (Exception e) {
+ // On close we swallow all random exceptions since failure to close is not
+ // actionable by the user.
+ Log.e(TAG, "Failed to close " + this + ", Exception=" + e);
+ } finally {
+ mResourceId = INVALID_RESOURCE_ID;
+ mCloseGuard.close();
}
- mCloseGuard.close();
}
/** Check that the Interface was closed properly. */
@@ -788,6 +852,17 @@
public int getResourceId() {
return mResourceId;
}
+
+ @Override
+ public String toString() {
+ return new StringBuilder()
+ .append("IpSecTunnelInterface{ifname=")
+ .append(mInterfaceName)
+ .append(",resourceId=")
+ .append(mResourceId)
+ .append("}")
+ .toString();
+ }
}
/**
@@ -810,8 +885,12 @@
public IpSecTunnelInterface createIpSecTunnelInterface(@NonNull InetAddress localAddress,
@NonNull InetAddress remoteAddress, @NonNull Network underlyingNetwork)
throws ResourceUnavailableException, IOException {
- return new IpSecTunnelInterface(
- mContext, mService, localAddress, remoteAddress, underlyingNetwork);
+ try {
+ return new IpSecTunnelInterface(
+ mContext, mService, localAddress, remoteAddress, underlyingNetwork);
+ } catch (ServiceSpecificException e) {
+ throw rethrowCheckedExceptionFromServiceSpecificException(e);
+ }
}
/**
@@ -838,6 +917,8 @@
mService.applyTunnelModeTransform(
tunnel.getResourceId(), direction,
transform.getResourceId(), mContext.getOpPackageName());
+ } catch (ServiceSpecificException e) {
+ throw rethrowCheckedExceptionFromServiceSpecificException(e);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -853,4 +934,44 @@
mContext = ctx;
mService = checkNotNull(service, "missing service");
}
+
+ private static void maybeHandleServiceSpecificException(ServiceSpecificException sse) {
+ // OsConstants are late binding, so switch statements can't be used.
+ if (sse.errorCode == OsConstants.EINVAL) {
+ throw new IllegalArgumentException(sse);
+ } else if (sse.errorCode == OsConstants.EAGAIN) {
+ throw new IllegalStateException(sse);
+ } else if (sse.errorCode == OsConstants.EOPNOTSUPP) {
+ throw new UnsupportedOperationException(sse);
+ }
+ }
+
+ /**
+ * Convert an Errno SSE to the correct Unchecked exception type.
+ *
+ * This method never actually returns.
+ */
+ // package
+ static RuntimeException
+ rethrowUncheckedExceptionFromServiceSpecificException(ServiceSpecificException sse) {
+ maybeHandleServiceSpecificException(sse);
+ throw new RuntimeException(sse);
+ }
+
+ /**
+ * Convert an Errno SSE to the correct Checked or Unchecked exception type.
+ *
+ * This method may throw IOException, or it may throw an unchecked exception; it will never
+ * actually return.
+ */
+ // package
+ static IOException rethrowCheckedExceptionFromServiceSpecificException(
+ ServiceSpecificException sse) throws IOException {
+ // First see if this is an unchecked exception of a type we know.
+ // If so, then we prefer the unchecked (specific) type of exception.
+ maybeHandleServiceSpecificException(sse);
+ // If not, then all we can do is provide the SSE in the form of an IOException.
+ throw new ErrnoException(
+ "IpSec encountered errno=" + sse.errorCode, sse.errorCode).rethrowAsIOException();
+ }
}
diff --git a/core/java/android/net/IpSecTransform.java b/core/java/android/net/IpSecTransform.java
index 62f7996..a12df28 100644
--- a/core/java/android/net/IpSecTransform.java
+++ b/core/java/android/net/IpSecTransform.java
@@ -28,6 +28,7 @@
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.ServiceSpecificException;
import android.util.Log;
import com.android.internal.annotations.VisibleForTesting;
@@ -136,6 +137,8 @@
mResourceId = result.resourceId;
Log.d(TAG, "Added Transform with Id " + mResourceId);
mCloseGuard.open("build");
+ } catch (ServiceSpecificException e) {
+ throw IpSecManager.rethrowUncheckedExceptionFromServiceSpecificException(e);
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
@@ -180,6 +183,10 @@
stopNattKeepalive();
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
+ } catch (Exception e) {
+ // On close we swallow all random exceptions since failure to close is not
+ // actionable by the user.
+ Log.e(TAG, "Failed to close " + this + ", Exception=" + e);
} finally {
mResourceId = INVALID_RESOURCE_ID;
mCloseGuard.close();
@@ -502,4 +509,13 @@
mConfig = new IpSecConfig();
}
}
+
+ @Override
+ public String toString() {
+ return new StringBuilder()
+ .append("IpSecTransform{resourceId=")
+ .append(mResourceId)
+ .append("}")
+ .toString();
+ }
}
diff --git a/core/java/android/os/BaseBundle.java b/core/java/android/os/BaseBundle.java
index f5a7433..0fef78d 100644
--- a/core/java/android/os/BaseBundle.java
+++ b/core/java/android/os/BaseBundle.java
@@ -40,8 +40,9 @@
private static final String TAG = "Bundle";
static final boolean DEBUG = false;
- // Keep in sync with frameworks/native/libs/binder/PersistableBundle.cpp.
- static final int BUNDLE_MAGIC = 0x4C444E42; // 'B' 'N' 'D' 'L'
+ // Keep them in sync with frameworks/native/libs/binder/PersistableBundle.cpp.
+ private static final int BUNDLE_MAGIC = 0x4C444E42; // 'B' 'N' 'D' 'L'
+ private static final int BUNDLE_MAGIC_NATIVE = 0x4C444E44; // 'B' 'N' 'D' 'N'
/**
* Flag indicating that this Bundle is okay to "defuse." That is, it's okay
@@ -91,6 +92,11 @@
Parcel mParcelledData = null;
/**
+ * Whether {@link #mParcelledData} was generated by native coed or not.
+ */
+ private boolean mParcelledByNative;
+
+ /**
* The ClassLoader used when unparcelling data from mParcelledData.
*/
private ClassLoader mClassLoader;
@@ -223,7 +229,7 @@
synchronized (this) {
final Parcel source = mParcelledData;
if (source != null) {
- initializeFromParcelLocked(source, /*recycleParcel=*/ true);
+ initializeFromParcelLocked(source, /*recycleParcel=*/ true, mParcelledByNative);
} else {
if (DEBUG) {
Log.d(TAG, "unparcel "
@@ -234,7 +240,8 @@
}
}
- private void initializeFromParcelLocked(@NonNull Parcel parcelledData, boolean recycleParcel) {
+ private void initializeFromParcelLocked(@NonNull Parcel parcelledData, boolean recycleParcel,
+ boolean parcelledByNative) {
if (LOG_DEFUSABLE && sShouldDefuse && (mFlags & FLAG_DEFUSABLE) == 0) {
Slog.wtf(TAG, "Attempting to unparcel a Bundle while in transit; this may "
+ "clobber all data inside!", new Throwable());
@@ -251,6 +258,7 @@
mMap.erase();
}
mParcelledData = null;
+ mParcelledByNative = false;
return;
}
@@ -270,7 +278,15 @@
map.ensureCapacity(count);
}
try {
- parcelledData.readArrayMapInternal(map, count, mClassLoader);
+ if (parcelledByNative) {
+ // If it was parcelled by native code, then the array map keys aren't sorted
+ // by their hash codes, so use the safe (slow) one.
+ parcelledData.readArrayMapSafelyInternal(map, count, mClassLoader);
+ } else {
+ // If parcelled by Java, we know the contents are sorted properly,
+ // so we can use ArrayMap.append().
+ parcelledData.readArrayMapInternal(map, count, mClassLoader);
+ }
} catch (BadParcelableException e) {
if (sShouldDefuse) {
Log.w(TAG, "Failed to parse Bundle, but defusing quietly", e);
@@ -284,6 +300,7 @@
recycleParcel(parcelledData);
}
mParcelledData = null;
+ mParcelledByNative = false;
}
if (DEBUG) {
Log.d(TAG, "unparcel " + Integer.toHexString(System.identityHashCode(this))
@@ -403,14 +420,17 @@
if (from.mParcelledData != null) {
if (from.isEmptyParcel()) {
mParcelledData = NoImagePreloadHolder.EMPTY_PARCEL;
+ mParcelledByNative = false;
} else {
mParcelledData = Parcel.obtain();
mParcelledData.appendFrom(from.mParcelledData, 0,
from.mParcelledData.dataSize());
mParcelledData.setDataPosition(0);
+ mParcelledByNative = from.mParcelledByNative;
}
} else {
mParcelledData = null;
+ mParcelledByNative = false;
}
if (from.mMap != null) {
@@ -1538,7 +1558,7 @@
} else {
int length = mParcelledData.dataSize();
parcel.writeInt(length);
- parcel.writeInt(BUNDLE_MAGIC);
+ parcel.writeInt(mParcelledByNative ? BUNDLE_MAGIC_NATIVE : BUNDLE_MAGIC);
parcel.appendFrom(mParcelledData, 0, length);
}
return;
@@ -1585,11 +1605,14 @@
} else if (length == 0) {
// Empty Bundle or end of data.
mParcelledData = NoImagePreloadHolder.EMPTY_PARCEL;
+ mParcelledByNative = false;
return;
}
final int magic = parcel.readInt();
- if (magic != BUNDLE_MAGIC) {
+ final boolean isJavaBundle = magic == BUNDLE_MAGIC;
+ final boolean isNativeBundle = magic == BUNDLE_MAGIC_NATIVE;
+ if (!isJavaBundle && !isNativeBundle) {
throw new IllegalStateException("Bad magic number for Bundle: 0x"
+ Integer.toHexString(magic));
}
@@ -1598,7 +1621,7 @@
// If the parcel has a read-write helper, then we can't lazily-unparcel it, so just
// unparcel right away.
synchronized (this) {
- initializeFromParcelLocked(parcel, /*recycleParcel=*/ false);
+ initializeFromParcelLocked(parcel, /*recycleParcel=*/ false, isNativeBundle);
}
return;
}
@@ -1616,6 +1639,7 @@
p.setDataPosition(0);
mParcelledData = p;
+ mParcelledByNative = isNativeBundle;
}
/** {@hide} */
diff --git a/core/java/android/os/VibrationEffect.java b/core/java/android/os/VibrationEffect.java
index e9b4853..4d238c0 100644
--- a/core/java/android/os/VibrationEffect.java
+++ b/core/java/android/os/VibrationEffect.java
@@ -349,6 +349,26 @@
return new OneShot(mDuration, newAmplitude);
}
+ /**
+ * Resolve default values into integer amplitude numbers.
+ *
+ * @param defaultAmplitude the default amplitude to apply, must be between 0 and
+ * MAX_AMPLITUDE
+ * @return A {@link OneShot} effect with same physical meaning but explicitly set amplitude
+ *
+ * @hide
+ */
+ public OneShot resolve(int defaultAmplitude) {
+ if (defaultAmplitude > MAX_AMPLITUDE || defaultAmplitude < 0) {
+ throw new IllegalArgumentException(
+ "Amplitude is negative or greater than MAX_AMPLITUDE");
+ }
+ if (mAmplitude == DEFAULT_AMPLITUDE) {
+ return new OneShot(mDuration, defaultAmplitude);
+ }
+ return this;
+ }
+
@Override
public void validate() {
if (mAmplitude < -1 || mAmplitude == 0 || mAmplitude > 255) {
@@ -470,6 +490,30 @@
return new Waveform(mTimings, scaledAmplitudes, mRepeat);
}
+ /**
+ * Resolve default values into integer amplitude numbers.
+ *
+ * @param defaultAmplitude the default amplitude to apply, must be between 0 and
+ * MAX_AMPLITUDE
+ * @return A {@link Waveform} effect with same physical meaning but explicitly set
+ * amplitude
+ *
+ * @hide
+ */
+ public Waveform resolve(int defaultAmplitude) {
+ if (defaultAmplitude > MAX_AMPLITUDE || defaultAmplitude < 0) {
+ throw new IllegalArgumentException(
+ "Amplitude is negative or greater than MAX_AMPLITUDE");
+ }
+ int[] resolvedAmplitudes = Arrays.copyOf(mAmplitudes, mAmplitudes.length);
+ for (int i = 0; i < resolvedAmplitudes.length; i++) {
+ if (resolvedAmplitudes[i] == DEFAULT_AMPLITUDE) {
+ resolvedAmplitudes[i] = defaultAmplitude;
+ }
+ }
+ return new Waveform(mTimings, resolvedAmplitudes, mRepeat);
+ }
+
@Override
public void validate() {
if (mTimings.length != mAmplitudes.length) {
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 6b6e14f..4d7c202 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -10776,6 +10776,7 @@
* read_binary_cpu_time (boolean)
* proc_state_cpu_times_read_delay_ms (long)
* external_stats_collection_rate_limit_ms (long)
+ * battery_level_collection_delay_ms (long)
* </pre>
*
* <p>
@@ -10796,15 +10797,28 @@
public static final String SYNC_MANAGER_CONSTANTS = "sync_manager_constants";
/**
- * Whether or not App Standby feature is enabled. This controls throttling of apps
- * based on usage patterns and predictions.
+ * Whether or not App Standby feature is enabled by system. This controls throttling of apps
+ * based on usage patterns and predictions. Platform will turn on this feature if both this
+ * flag and {@link #ADAPTIVE_BATTERY_MANAGEMENT_ENABLED} is on.
* Type: int (0 for false, 1 for true)
* Default: 1
* @hide
+ * @see #ADAPTIVE_BATTERY_MANAGEMENT_ENABLED
*/
public static final String APP_STANDBY_ENABLED = "app_standby_enabled";
/**
+ * Whether or not adaptive battery feature is enabled by user. Platform will turn on this
+ * feature if both this flag and {@link #APP_STANDBY_ENABLED} is on.
+ * Type: int (0 for false, 1 for true)
+ * Default: 1
+ * @hide
+ * @see #APP_STANDBY_ENABLED
+ */
+ public static final String ADAPTIVE_BATTERY_MANAGEMENT_ENABLED =
+ "adaptive_battery_management_enabled";
+
+ /**
* Whether or not app auto restriction is enabled. When it is enabled, settings app will
* auto restrict the app if it has bad behavior(e.g. hold wakelock for long time).
*
diff --git a/core/java/android/service/autofill/BatchUpdates.java b/core/java/android/service/autofill/BatchUpdates.java
index 90acc88..2ba0376 100644
--- a/core/java/android/service/autofill/BatchUpdates.java
+++ b/core/java/android/service/autofill/BatchUpdates.java
@@ -82,6 +82,9 @@
* {@link #transformChild(int, Transformation) transformations} are applied to the children
* views.
*
+ * <p>Theme does not work with RemoteViews layout. Avoid hardcoded text color
+ * or background color: Autofill on different platforms may have different themes.
+ *
* @param updates a {@link RemoteViews} with the updated actions to be applied in the
* underlying presentation template.
*
diff --git a/core/java/android/service/autofill/Dataset.java b/core/java/android/service/autofill/Dataset.java
index ccec483..5211767 100644
--- a/core/java/android/service/autofill/Dataset.java
+++ b/core/java/android/service/autofill/Dataset.java
@@ -336,6 +336,9 @@
* higher, datasets that require authentication can be also be filtered by passing a
* {@link AutofillValue#forText(CharSequence) text value} as the {@code value} parameter.
*
+ * <p>Theme does not work with RemoteViews layout. Avoid hardcoded text color
+ * or background color: Autofill on different platforms may have different themes.
+ *
* @param id id returned by {@link
* android.app.assist.AssistStructure.ViewNode#getAutofillId()}.
* @param value the value to be autofilled. Pass {@code null} if you do not have the value
diff --git a/core/java/android/service/autofill/FillResponse.java b/core/java/android/service/autofill/FillResponse.java
index 2bc4b8f..7bf1f83 100644
--- a/core/java/android/service/autofill/FillResponse.java
+++ b/core/java/android/service/autofill/FillResponse.java
@@ -241,6 +241,9 @@
* immutable by using {@link android.app.PendingIntent#FLAG_IMMUTABLE} as the
* platform needs to fill in the authentication arguments.
*
+ * <p>Theme does not work with RemoteViews layout. Avoid hardcoded text color
+ * or background color: Autofill on different platforms may have different themes.
+ *
* @param authentication Intent to an activity with your authentication flow.
* @param presentation The presentation to visualize the response.
* @param ids id of Views that when focused will display the authentication UI.
@@ -449,6 +452,9 @@
* authentication (as the header could have been set directly in the main presentation in
* these cases).
*
+ * <p>Theme does not work with RemoteViews layout. Avoid hardcoded text color
+ * or background color: Autofill on different platforms may have different themes.
+ *
* @param header a presentation to represent the header. This presentation is not clickable
* —calling
* {@link RemoteViews#setOnClickPendingIntent(int, android.app.PendingIntent)} on it would
@@ -477,6 +483,9 @@
* authentication (as the footer could have been set directly in the main presentation in
* these cases).
*
+ * <p>Theme does not work with RemoteViews layout. Avoid hardcoded text color
+ * or background color: Autofill on different platforms may have different themes.
+ *
* @param footer a presentation to represent the footer. This presentation is not clickable
* —calling
* {@link RemoteViews#setOnClickPendingIntent(int, android.app.PendingIntent)} on it would
diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index 03f1c12..1c2e43e 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -2413,11 +2413,16 @@
/**
* Returns whether node represents a heading.
+ * <p><strong>Note:</strong> Returns {@code true} if either {@link #setHeading(boolean)}
+ * marks this node as a heading or if the node has a {@link CollectionItemInfo} that marks
+ * it as such, to accomodate apps that use the now-deprecated API.</p>
*
* @return {@code true} if the node is a heading, {@code false} otherwise.
*/
public boolean isHeading() {
- return getBooleanProperty(BOOLEAN_PROPERTY_IS_HEADING);
+ if (getBooleanProperty(BOOLEAN_PROPERTY_IS_HEADING)) return true;
+ CollectionItemInfo itemInfo = getCollectionItemInfo();
+ return ((itemInfo != null) && itemInfo.mHeading);
}
/**
@@ -3437,6 +3442,7 @@
mPackageName = other.mPackageName;
mClassName = other.mClassName;
mText = other.mText;
+ mOriginalText = other.mOriginalText;
mHintText = other.mHintText;
mError = other.mError;
mContentDescription = other.mContentDescription;
diff --git a/core/java/android/view/autofill/AutofillPopupWindow.java b/core/java/android/view/autofill/AutofillPopupWindow.java
index a6495d1..8266207 100644
--- a/core/java/android/view/autofill/AutofillPopupWindow.java
+++ b/core/java/android/view/autofill/AutofillPopupWindow.java
@@ -19,6 +19,7 @@
import static android.view.autofill.Helper.sVerbose;
import android.annotation.NonNull;
+import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.IBinder;
@@ -79,11 +80,6 @@
public AutofillPopupWindow(@NonNull IAutofillWindowPresenter presenter) {
mWindowPresenter = new WindowPresenter(presenter);
- // We want to show the window as system controlled one so it covers app windows, but it has
- // to be an application type (so it's contained inside the application area).
- // Hence, we set it to the application type with the highest z-order, which currently
- // is TYPE_APPLICATION_ABOVE_SUB_PANEL.
- setWindowLayoutType(WindowManager.LayoutParams.TYPE_APPLICATION_ABOVE_SUB_PANEL);
setTouchModal(false);
setOutsideTouchable(true);
setInputMethodMode(INPUT_METHOD_NOT_NEEDED);
@@ -110,7 +106,16 @@
*/
public void update(View anchor, int offsetX, int offsetY, int width, int height,
Rect virtualBounds) {
- mFullScreen = width == LayoutParams.MATCH_PARENT && height == LayoutParams.MATCH_PARENT;
+ mFullScreen = width == LayoutParams.MATCH_PARENT;
+ // For no fullscreen autofill window, we want to show the window as system controlled one
+ // so it covers app windows, but it has to be an application type (so it's contained inside
+ // the application area). Hence, we set it to the application type with the highest z-order,
+ // which currently is TYPE_APPLICATION_ABOVE_SUB_PANEL.
+ // For fullscreen mode, autofill window is at the bottom of screen, it should not be
+ // clipped by app activity window. Fullscreen autofill window does not need to follow app
+ // anchor view position.
+ setWindowLayoutType(mFullScreen ? WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG
+ : WindowManager.LayoutParams.TYPE_APPLICATION_ABOVE_SUB_PANEL);
// If we are showing the popup for a virtual view we use a fake view which
// delegates to the anchor but present itself with the same bounds as the
// virtual view. This ensures that the location logic in popup works
@@ -119,6 +124,15 @@
if (mFullScreen) {
offsetX = 0;
offsetY = 0;
+ // If it is not fullscreen height, put window at bottom. Computes absolute position.
+ // Note that we cannot easily change default gravity from Gravity.TOP to
+ // Gravity.BOTTOM because PopupWindow base class does not expose computeGravity().
+ final Point outPoint = new Point();
+ anchor.getContext().getDisplay().getSize(outPoint);
+ width = outPoint.x;
+ if (height != LayoutParams.MATCH_PARENT) {
+ offsetY = outPoint.y - height;
+ }
actualAnchor = anchor;
} else if (virtualBounds != null) {
final int[] mLocationOnScreen = new int[] {virtualBounds.left, virtualBounds.top};
@@ -202,6 +216,16 @@
actualAnchor = anchor;
}
+ if (!mFullScreen) {
+ // No fullscreen window animation is controlled by PopupWindow.
+ setAnimationStyle(-1);
+ } else if (height == LayoutParams.MATCH_PARENT) {
+ // Complete fullscreen autofill window has no animation.
+ setAnimationStyle(0);
+ } else {
+ // Slide half screen height autofill window from bottom.
+ setAnimationStyle(com.android.internal.R.style.AutofillHalfScreenAnimation);
+ }
if (!isShowing()) {
setWidth(width);
setHeight(height);
@@ -223,7 +247,12 @@
protected boolean findDropDownPosition(View anchor, LayoutParams outParams,
int xOffset, int yOffset, int width, int height, int gravity, boolean allowScroll) {
if (mFullScreen) {
- // Do not patch LayoutParams if force full screen
+ // In fullscreen mode, don't need consider the anchor view.
+ outParams.x = xOffset;
+ outParams.y = yOffset;
+ outParams.width = width;
+ outParams.height = height;
+ outParams.gravity = gravity;
return false;
}
return super.findDropDownPosition(anchor, outParams, xOffset, yOffset,
@@ -316,11 +345,6 @@
}
@Override
- public void setAnimationStyle(int animationStyle) {
- throw new IllegalStateException("You can't call this!");
- }
-
- @Override
public void setBackgroundDrawable(Drawable background) {
throw new IllegalStateException("You can't call this!");
}
diff --git a/core/java/android/widget/Magnifier.java b/core/java/android/widget/Magnifier.java
index cb362e6..f1a1457 100644
--- a/core/java/android/widget/Magnifier.java
+++ b/core/java/android/widget/Magnifier.java
@@ -170,7 +170,7 @@
if (mWindow == null) {
synchronized (mLock) {
mWindow = new InternalPopupWindow(mView.getContext(), mView.getDisplay(),
- getValidViewSurface(),
+ getValidParentSurfaceForMagnifier(),
mWindowWidth, mWindowHeight, mWindowElevation, mWindowCornerRadius,
Handler.getMain() /* draw the magnifier on the UI thread */, mLock,
mCallback);
@@ -245,18 +245,20 @@
}
@Nullable
- private Surface getValidViewSurface() {
- // TODO: deduplicate this against the first part of #performPixelCopy
- final Surface surface;
- if (mView instanceof SurfaceView) {
- surface = ((SurfaceView) mView).getHolder().getSurface();
- } else if (mView.getViewRootImpl() != null) {
- surface = mView.getViewRootImpl().mSurface;
- } else {
- surface = null;
+ private Surface getValidParentSurfaceForMagnifier() {
+ if (mView.getViewRootImpl() != null) {
+ final Surface mainWindowSurface = mView.getViewRootImpl().mSurface;
+ if (mainWindowSurface != null && mainWindowSurface.isValid()) {
+ return mainWindowSurface;
+ }
}
-
- return (surface != null && surface.isValid()) ? surface : null;
+ if (mView instanceof SurfaceView) {
+ final Surface surfaceViewSurface = ((SurfaceView) mView).getHolder().getSurface();
+ if (surfaceViewSurface != null && surfaceViewSurface.isValid()) {
+ return surfaceViewSurface;
+ }
+ }
+ return null;
}
private void configureCoordinates(final float xPosInView, final float yPosInView) {
@@ -264,12 +266,12 @@
// magnifier. These are relative to the surface the content is copied from.
final float posX;
final float posY;
+ mView.getLocationInSurface(mViewCoordinatesInSurface);
if (mView instanceof SurfaceView) {
// No offset required if the backing Surface matches the size of the SurfaceView.
posX = xPosInView;
posY = yPosInView;
} else {
- mView.getLocationInSurface(mViewCoordinatesInSurface);
posX = xPosInView + mViewCoordinatesInSurface[0];
posY = yPosInView + mViewCoordinatesInSurface[1];
}
@@ -282,6 +284,14 @@
R.dimen.magnifier_offset);
mWindowCoords.x = mCenterZoomCoords.x - mWindowWidth / 2;
mWindowCoords.y = mCenterZoomCoords.y - mWindowHeight / 2 - verticalOffset;
+ if (mView instanceof SurfaceView && mView.getViewRootImpl() != null) {
+ // TODO: deduplicate against the first part of #getValidParentSurfaceForMagnifier()
+ final Surface mainWindowSurface = mView.getViewRootImpl().mSurface;
+ if (mainWindowSurface != null && mainWindowSurface.isValid()) {
+ mWindowCoords.x += mViewCoordinatesInSurface[0];
+ mWindowCoords.y += mViewCoordinatesInSurface[1];
+ }
+ }
}
private void performPixelCopy(final int startXInSurface, final int startYInSurface,
@@ -361,6 +371,9 @@
// The alpha set on the magnifier's content, which defines how
// prominent the white background is.
private static final int CONTENT_BITMAP_ALPHA = 242;
+ // The z of the magnifier surface, defining its z order in the list of
+ // siblings having the same parent surface (usually the main app surface).
+ private static final int SURFACE_Z = 5;
// Display associated to the view the magnifier is attached to.
private final Display mDisplay;
@@ -602,6 +615,7 @@
mSurfaceControl.setPosition(pendingX, pendingY);
}
if (firstDraw) {
+ mSurfaceControl.setLayer(SURFACE_Z);
mSurfaceControl.show();
}
SurfaceControl.closeTransaction();
diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java
index 5ecbf90a..4865dab 100644
--- a/core/java/android/widget/RemoteViews.java
+++ b/core/java/android/widget/RemoteViews.java
@@ -21,6 +21,7 @@
import android.annotation.ColorInt;
import android.annotation.DimenRes;
import android.annotation.NonNull;
+import android.annotation.StyleRes;
import android.app.ActivityOptions;
import android.app.ActivityThread;
import android.app.Application;
@@ -56,6 +57,7 @@
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Log;
+import android.view.ContextThemeWrapper;
import android.view.LayoutInflater;
import android.view.LayoutInflater.Filter;
import android.view.RemotableViewMethod;
@@ -182,6 +184,12 @@
private boolean mIsRoot = true;
/**
+ * Optional theme resource id applied in inflateView(). When 0, Theme.DeviceDefault will be
+ * used.
+ */
+ private int mApplyThemeResId;
+
+ /**
* Whether reapply is disallowed on this remoteview. This maybe be true if some actions modify
* the layout in a way that isn't recoverable, since views are being removed.
*/
@@ -3267,6 +3275,14 @@
}
/**
+ * Set the theme used in apply() and applyASync().
+ * @hide
+ */
+ public void setApplyTheme(@StyleRes int themeResId) {
+ mApplyThemeResId = themeResId;
+ }
+
+ /**
* Inflates the view hierarchy represented by this object and applies
* all of the actions.
*
@@ -3301,6 +3317,10 @@
final Context contextForResources = getContextForResources(context);
Context inflationContext = new RemoteViewsContextWrapper(context, contextForResources);
+ // If mApplyThemeResId is not given, Theme.DeviceDefault will be used.
+ if (mApplyThemeResId != 0) {
+ inflationContext = new ContextThemeWrapper(inflationContext, mApplyThemeResId);
+ }
LayoutInflater inflater = (LayoutInflater)
context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
diff --git a/core/java/android/widget/StackView.java b/core/java/android/widget/StackView.java
index 1b9055c..6cc86b9 100644
--- a/core/java/android/widget/StackView.java
+++ b/core/java/android/widget/StackView.java
@@ -28,7 +28,6 @@
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.RectF;
-import android.graphics.Region;
import android.graphics.TableMaskFilter;
import android.os.Bundle;
import android.util.AttributeSet;
@@ -550,8 +549,8 @@
// We only expand the clip bounds if necessary.
if (expandClipRegion) {
- canvas.save(Canvas.CLIP_SAVE_FLAG);
- canvas.clipRect(stackInvalidateRect, Region.Op.UNION);
+ canvas.save();
+ canvas.clipRectUnion(stackInvalidateRect);
super.dispatchDraw(canvas);
canvas.restore();
} else {
diff --git a/core/java/com/android/internal/app/AlertController.java b/core/java/com/android/internal/app/AlertController.java
index 46cb546..7321721 100644
--- a/core/java/com/android/internal/app/AlertController.java
+++ b/core/java/com/android/internal/app/AlertController.java
@@ -30,7 +30,10 @@
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.os.Message;
+import android.text.Layout;
import android.text.TextUtils;
+import android.text.method.LinkMovementMethod;
+import android.text.method.MovementMethod;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.Gravity;
@@ -101,6 +104,9 @@
private ImageView mIconView;
private TextView mTitleView;
protected TextView mMessageView;
+ private MovementMethod mMessageMovementMethod;
+ @Layout.HyphenationFrequency
+ private Integer mMessageHyphenationFrequency;
private View mCustomTitleView;
private boolean mForceInverseBackground;
@@ -290,6 +296,21 @@
}
}
+ public void setMessageMovementMethod(MovementMethod movementMethod) {
+ mMessageMovementMethod = movementMethod;
+ if (mMessageView != null) {
+ mMessageView.setMovementMethod(movementMethod);
+ }
+ }
+
+ public void setMessageHyphenationFrequency(
+ @Layout.HyphenationFrequency int hyphenationFrequency) {
+ mMessageHyphenationFrequency = hyphenationFrequency;
+ if (mMessageView != null) {
+ mMessageView.setHyphenationFrequency(hyphenationFrequency);
+ }
+ }
+
/**
* Set the view resource to display in the dialog.
*/
@@ -676,6 +697,12 @@
if (mMessage != null) {
mMessageView.setText(mMessage);
+ if (mMessageMovementMethod != null) {
+ mMessageView.setMovementMethod(mMessageMovementMethod);
+ }
+ if (mMessageHyphenationFrequency != null) {
+ mMessageView.setHyphenationFrequency(mMessageHyphenationFrequency);
+ }
} else {
mMessageView.setVisibility(View.GONE);
mScrollView.removeView(mMessageView);
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index a4680ca..be83498 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -585,6 +585,7 @@
boolean onBatteryScreenOff);
Future<?> scheduleCpuSyncDueToWakelockChange(long delayMillis);
void cancelCpuSyncDueToWakelockChange();
+ Future<?> scheduleSyncDueToBatteryLevelChange(long delayMillis);
}
public Handler mHandler;
@@ -12614,7 +12615,8 @@
// TODO(adamlesinski): Schedule the creation of a HistoryStepDetails record
// which will pull external stats.
- scheduleSyncExternalStatsLocked("battery-level", ExternalStatsSync.UPDATE_ALL);
+ mExternalSync.scheduleSyncDueToBatteryLevelChange(
+ mConstants.BATTERY_LEVEL_COLLECTION_DELAY_MS);
}
if (mHistoryCur.batteryStatus != status) {
mHistoryCur.batteryStatus = (byte)status;
@@ -13270,6 +13272,8 @@
= "uid_remove_delay_ms";
public static final String KEY_EXTERNAL_STATS_COLLECTION_RATE_LIMIT_MS
= "external_stats_collection_rate_limit_ms";
+ public static final String KEY_BATTERY_LEVEL_COLLECTION_DELAY_MS
+ = "battery_level_collection_delay_ms";
private static final boolean DEFAULT_TRACK_CPU_TIMES_BY_PROC_STATE = true;
private static final boolean DEFAULT_TRACK_CPU_ACTIVE_CLUSTER_TIME = true;
@@ -13277,6 +13281,7 @@
private static final long DEFAULT_KERNEL_UID_READERS_THROTTLE_TIME = 10_000;
private static final long DEFAULT_UID_REMOVE_DELAY_MS = 5L * 60L * 1000L;
private static final long DEFAULT_EXTERNAL_STATS_COLLECTION_RATE_LIMIT_MS = 600_000;
+ private static final long DEFAULT_BATTERY_LEVEL_COLLECTION_DELAY_MS = 300_000;
public boolean TRACK_CPU_TIMES_BY_PROC_STATE = DEFAULT_TRACK_CPU_TIMES_BY_PROC_STATE;
public boolean TRACK_CPU_ACTIVE_CLUSTER_TIME = DEFAULT_TRACK_CPU_ACTIVE_CLUSTER_TIME;
@@ -13285,6 +13290,8 @@
public long UID_REMOVE_DELAY_MS = DEFAULT_UID_REMOVE_DELAY_MS;
public long EXTERNAL_STATS_COLLECTION_RATE_LIMIT_MS
= DEFAULT_EXTERNAL_STATS_COLLECTION_RATE_LIMIT_MS;
+ public long BATTERY_LEVEL_COLLECTION_DELAY_MS
+ = DEFAULT_BATTERY_LEVEL_COLLECTION_DELAY_MS;
private ContentResolver mResolver;
private final KeyValueListParser mParser = new KeyValueListParser(',');
@@ -13333,6 +13340,9 @@
EXTERNAL_STATS_COLLECTION_RATE_LIMIT_MS = mParser.getLong(
KEY_EXTERNAL_STATS_COLLECTION_RATE_LIMIT_MS,
DEFAULT_EXTERNAL_STATS_COLLECTION_RATE_LIMIT_MS);
+ BATTERY_LEVEL_COLLECTION_DELAY_MS = mParser.getLong(
+ KEY_BATTERY_LEVEL_COLLECTION_DELAY_MS,
+ DEFAULT_BATTERY_LEVEL_COLLECTION_DELAY_MS);
}
}
@@ -13384,6 +13394,8 @@
pw.println(KERNEL_UID_READERS_THROTTLE_TIME);
pw.print(KEY_EXTERNAL_STATS_COLLECTION_RATE_LIMIT_MS); pw.print("=");
pw.println(EXTERNAL_STATS_COLLECTION_RATE_LIMIT_MS);
+ pw.print(KEY_BATTERY_LEVEL_COLLECTION_DELAY_MS); pw.print("=");
+ pw.println(BATTERY_LEVEL_COLLECTION_DELAY_MS);
}
}
diff --git a/core/java/com/android/internal/widget/FloatingToolbar.java b/core/java/com/android/internal/widget/FloatingToolbar.java
index 63c2e96..f70c554 100644
--- a/core/java/com/android/internal/widget/FloatingToolbar.java
+++ b/core/java/com/android/internal/widget/FloatingToolbar.java
@@ -452,7 +452,7 @@
mLineHeight = context.getResources()
.getDimensionPixelSize(R.dimen.floating_toolbar_height);
mIconTextSpacing = context.getResources()
- .getDimensionPixelSize(R.dimen.floating_toolbar_menu_button_side_padding);
+ .getDimensionPixelSize(R.dimen.floating_toolbar_icon_text_spacing);
// Interpolators
mLogAccelerateInterpolator = new LogAccelerateInterpolator();
@@ -481,7 +481,7 @@
mOverflowButton = createOverflowButton();
mOverflowButtonSize = measure(mOverflowButton);
mMainPanel = createMainPanel();
- mOverflowPanelViewHelper = new OverflowPanelViewHelper(mContext);
+ mOverflowPanelViewHelper = new OverflowPanelViewHelper(mContext, mIconTextSpacing);
mOverflowPanel = createOverflowPanel();
// Animation. Need views.
@@ -1573,10 +1573,9 @@
private final Context mContext;
- public OverflowPanelViewHelper(Context context) {
+ public OverflowPanelViewHelper(Context context, int iconTextSpacing) {
mContext = Preconditions.checkNotNull(context);
- mIconTextSpacing = context.getResources()
- .getDimensionPixelSize(R.dimen.floating_toolbar_menu_button_side_padding);
+ mIconTextSpacing = iconTextSpacing;
mSidePadding = context.getResources()
.getDimensionPixelSize(R.dimen.floating_toolbar_overflow_side_padding);
mCalculator = createMenuButton(null);
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index da494d4..8235507 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1424,6 +1424,13 @@
<permission android:name="android.permission.NETWORK_SETTINGS"
android:protectionLevel="signature" />
+ <!-- Allows SetupWizard to call methods in Networking services
+ <p>Not for use by any other third-party or privileged applications.
+ @hide This should only be used by SetupWizard.
+ -->
+ <permission android:name="android.permission.NETWORK_SETUP_WIZARD"
+ android:protectionLevel="signature|setup" />
+
<!-- #SystemApi @hide Allows applications to access information about LoWPAN interfaces.
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.ACCESS_LOWPAN_STATE"
diff --git a/core/res/res/anim-ldrtl/task_close_enter.xml b/core/res/res/anim-ldrtl/task_close_enter.xml
new file mode 100644
index 0000000..7abada3
--- /dev/null
+++ b/core/res/res/anim-ldrtl/task_close_enter.xml
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2018 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
+ -->
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shareInterpolator="false"
+ android:zAdjustment="top"
+ android:showWallpaper="true">
+
+ <alpha
+ android:fromAlpha="1"
+ android:toAlpha="1.0"
+ android:fillEnabled="true"
+ android:fillBefore="true"
+ android:fillAfter="true"
+ android:interpolator="@interpolator/linear"
+ android:startOffset="67"
+ android:duration="217"/>
+
+ <translate
+ android:fromXDelta="105%"
+ android:toXDelta="0"
+ android:fillEnabled="true"
+ android:fillBefore="true"
+ android:fillAfter="true"
+ android:interpolator="@interpolator/aggressive_ease"
+ android:startOffset="50"
+ android:duration="383"/>
+
+ <scale
+ android:fromXScale="1.0526"
+ android:toXScale="1"
+ android:fromYScale="1.0526"
+ android:toYScale="1"
+ android:pivotX="50%"
+ android:pivotY="50%"
+ android:fillEnabled="true"
+ android:fillBefore="true"
+ android:fillAfter="true"
+ android:interpolator="@interpolator/fast_out_slow_in"
+ android:duration="283"/>
+
+ <scale
+ android:fromXScale="0.95"
+ android:toXScale="1"
+ android:fromYScale="0.95"
+ android:toYScale="1"
+ android:pivotX="50%"
+ android:pivotY="50%"
+ android:fillEnabled="true"
+ android:fillBefore="true"
+ android:fillAfter="true"
+ android:interpolator="@interpolator/fast_out_slow_in"
+ android:startOffset="283"
+ android:duration="317"/>
+</set>
\ No newline at end of file
diff --git a/core/res/res/anim-ldrtl/task_close_exit.xml b/core/res/res/anim-ldrtl/task_close_exit.xml
new file mode 100644
index 0000000..a017820
--- /dev/null
+++ b/core/res/res/anim-ldrtl/task_close_exit.xml
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2018 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
+ -->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shareInterpolator="false"
+ android:showWallpaper="true">
+
+ <alpha
+ android:fromAlpha="1.0"
+ android:toAlpha="1"
+ android:fillEnabled="true"
+ android:fillBefore="true"
+ android:fillAfter="true"
+ android:interpolator="@interpolator/linear"
+ android:startOffset="67"
+ android:duration="283"/>
+
+ <translate
+ android:fromXDelta="0"
+ android:toXDelta="-105%"
+ android:fillEnabled="true"
+ android:fillBefore="true"
+ android:fillAfter="true"
+ android:interpolator="@interpolator/aggressive_ease"
+ android:startOffset="50"
+ android:duration="383"/>
+
+ <scale
+ android:fromXScale="1.0"
+ android:toXScale="0.95"
+ android:fromYScale="1.0"
+ android:toYScale="0.95"
+ android:fillEnabled="true"
+ android:fillBefore="true"
+ android:fillAfter="true"
+ android:pivotX="50%"
+ android:pivotY="50%"
+ android:interpolator="@interpolator/fast_out_slow_in"
+ android:duration="283"/>
+
+ <!-- This is needed to keep the animation running while task_open_enter completes -->
+ <alpha
+ android:fromAlpha="1.0"
+ android:toAlpha="1.0"
+ android:duration="600"/>
+</set>
\ No newline at end of file
diff --git a/core/res/res/anim-ldrtl/task_open_enter.xml b/core/res/res/anim-ldrtl/task_open_enter.xml
new file mode 100644
index 0000000..0433664
--- /dev/null
+++ b/core/res/res/anim-ldrtl/task_open_enter.xml
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2018 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
+ -->
+<!-- This should in sync with task_open_enter_cross_profile_apps.xml -->
+<!-- This should in sync with cross_profile_apps_thumbnail_enter.xml -->
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shareInterpolator="false"
+ android:zAdjustment="top"
+ android:showWallpaper="true">
+
+ <alpha
+ android:fromAlpha="1"
+ android:toAlpha="1.0"
+ android:fillEnabled="true"
+ android:fillBefore="true"
+ android:fillAfter="true"
+ android:interpolator="@interpolator/linear"
+ android:startOffset="67"
+ android:duration="217"/>
+
+ <translate
+ android:fromXDelta="-105%"
+ android:toXDelta="0"
+ android:fillEnabled="true"
+ android:fillBefore="true"
+ android:fillAfter="true"
+ android:interpolator="@interpolator/aggressive_ease"
+ android:startOffset="50"
+ android:duration="383"/>
+
+ <scale
+ android:fromXScale="1.0526"
+ android:toXScale="1"
+ android:fromYScale="1.0526"
+ android:toYScale="1"
+ android:pivotX="50%"
+ android:pivotY="50%"
+ android:fillEnabled="true"
+ android:fillBefore="true"
+ android:fillAfter="true"
+ android:interpolator="@interpolator/fast_out_slow_in"
+ android:duration="283"/>
+
+ <scale
+ android:fromXScale="0.95"
+ android:toXScale="1"
+ android:fromYScale="0.95"
+ android:toYScale="1"
+ android:pivotX="50%"
+ android:pivotY="50%"
+ android:fillEnabled="true"
+ android:fillBefore="true"
+ android:fillAfter="true"
+ android:interpolator="@interpolator/fast_out_slow_in"
+ android:startOffset="283"
+ android:duration="317"/>
+</set>
\ No newline at end of file
diff --git a/core/res/res/anim-ldrtl/task_open_enter_cross_profile_apps.xml b/core/res/res/anim-ldrtl/task_open_enter_cross_profile_apps.xml
new file mode 100644
index 0000000..45ca80e
--- /dev/null
+++ b/core/res/res/anim-ldrtl/task_open_enter_cross_profile_apps.xml
@@ -0,0 +1,77 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2018 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
+ -->
+<!-- This should in sync with task_open_enter.xml -->
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shareInterpolator="false"
+ android:zAdjustment="top"
+ android:showWallpaper="true">
+
+ <alpha
+ android:fromAlpha="1"
+ android:toAlpha="1.0"
+ android:fillEnabled="true"
+ android:fillBefore="true"
+ android:fillAfter="true"
+ android:interpolator="@interpolator/linear"
+ android:startOffset="67"
+ android:duration="217"/>
+
+ <translate
+ android:fromXDelta="-105%"
+ android:toXDelta="0"
+ android:fillEnabled="true"
+ android:fillBefore="true"
+ android:fillAfter="true"
+ android:interpolator="@interpolator/aggressive_ease"
+ android:startOffset="50"
+ android:duration="383"/>
+
+ <scale
+ android:fromXScale="1.0526"
+ android:toXScale="1"
+ android:fromYScale="1.0526"
+ android:toYScale="1"
+ android:pivotX="50%"
+ android:pivotY="50%"
+ android:fillEnabled="true"
+ android:fillBefore="true"
+ android:fillAfter="true"
+ android:interpolator="@interpolator/fast_out_slow_in"
+ android:duration="283"/>
+
+ <scale
+ android:fromXScale="0.95"
+ android:toXScale="1"
+ android:fromYScale="0.95"
+ android:toYScale="1"
+ android:pivotX="50%"
+ android:pivotY="50%"
+ android:fillEnabled="true"
+ android:fillBefore="true"
+ android:fillAfter="true"
+ android:interpolator="@interpolator/fast_out_slow_in"
+ android:startOffset="283"
+ android:duration="317"/>
+
+ <!-- To keep the transition around longer for the thumbnail, should be kept in sync with
+ cross_profile_apps_thumbmail.xml -->
+ <alpha
+ android:fromAlpha="1.0"
+ android:toAlpha="1.0"
+ android:startOffset="717"
+ android:duration="200"/>
+</set>
\ No newline at end of file
diff --git a/core/res/res/anim-ldrtl/task_open_exit.xml b/core/res/res/anim-ldrtl/task_open_exit.xml
new file mode 100644
index 0000000..f50494d
--- /dev/null
+++ b/core/res/res/anim-ldrtl/task_open_exit.xml
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2018 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
+ -->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shareInterpolator="false"
+ android:showWallpaper="true">
+
+ <alpha
+ android:fromAlpha="1.0"
+ android:toAlpha="1"
+ android:fillEnabled="true"
+ android:fillBefore="true"
+ android:fillAfter="true"
+ android:interpolator="@interpolator/linear"
+ android:startOffset="67"
+ android:duration="283"/>
+
+ <translate
+ android:fromXDelta="0"
+ android:toXDelta="105%"
+ android:fillEnabled="true"
+ android:fillBefore="true"
+ android:fillAfter="true"
+ android:interpolator="@interpolator/aggressive_ease"
+ android:startOffset="50"
+ android:duration="383"/>
+
+ <scale
+ android:fromXScale="1.0"
+ android:toXScale="0.95"
+ android:fromYScale="1.0"
+ android:toYScale="0.95"
+ android:fillEnabled="true"
+ android:fillBefore="true"
+ android:fillAfter="true"
+ android:pivotX="50%"
+ android:pivotY="50%"
+ android:interpolator="@interpolator/fast_out_slow_in"
+ android:duration="283"/>
+
+ <!-- This is needed to keep the animation running while task_open_enter completes -->
+ <alpha
+ android:fromAlpha="1.0"
+ android:toAlpha="1.0"
+ android:duration="600"/>
+</set>
\ No newline at end of file
diff --git a/core/res/res/anim/task_close_enter.xml b/core/res/res/anim/task_close_enter.xml
index c298b80..b059aa9 100644
--- a/core/res/res/anim/task_close_enter.xml
+++ b/core/res/res/anim/task_close_enter.xml
@@ -31,7 +31,7 @@
android:duration="217"/>
<translate
- android:fromXDelta="105%"
+ android:fromXDelta="-105%"
android:toXDelta="0"
android:fillEnabled="true"
android:fillBefore="true"
diff --git a/core/res/res/anim/task_close_exit.xml b/core/res/res/anim/task_close_exit.xml
index 9394c57..c9ade22 100644
--- a/core/res/res/anim/task_close_exit.xml
+++ b/core/res/res/anim/task_close_exit.xml
@@ -32,7 +32,7 @@
<translate
android:fromXDelta="0"
- android:toXDelta="-105%"
+ android:toXDelta="105%"
android:fillEnabled="true"
android:fillBefore="true"
android:fillAfter="true"
diff --git a/core/res/res/anim/task_open_enter.xml b/core/res/res/anim/task_open_enter.xml
index e23201f..5c61859 100644
--- a/core/res/res/anim/task_open_enter.xml
+++ b/core/res/res/anim/task_open_enter.xml
@@ -33,7 +33,7 @@
android:duration="217"/>
<translate
- android:fromXDelta="-105%"
+ android:fromXDelta="105%"
android:toXDelta="0"
android:fillEnabled="true"
android:fillBefore="true"
diff --git a/core/res/res/anim/task_open_enter_cross_profile_apps.xml b/core/res/res/anim/task_open_enter_cross_profile_apps.xml
index defea08..6441047 100644
--- a/core/res/res/anim/task_open_enter_cross_profile_apps.xml
+++ b/core/res/res/anim/task_open_enter_cross_profile_apps.xml
@@ -33,7 +33,7 @@
android:duration="217"/>
<translate
- android:fromXDelta="-105%"
+ android:fromXDelta="105%"
android:toXDelta="0"
android:fillEnabled="true"
android:fillBefore="true"
diff --git a/core/res/res/anim/task_open_exit.xml b/core/res/res/anim/task_open_exit.xml
index c9ade22..9394c57 100644
--- a/core/res/res/anim/task_open_exit.xml
+++ b/core/res/res/anim/task_open_exit.xml
@@ -32,7 +32,7 @@
<translate
android:fromXDelta="0"
- android:toXDelta="105%"
+ android:toXDelta="-105%"
android:fillEnabled="true"
android:fillBefore="true"
android:fillAfter="true"
diff --git a/core/res/res/layout-television/autofill_save.xml b/core/res/res/layout-television/autofill_save.xml
new file mode 100644
index 0000000..ebd2dec
--- /dev/null
+++ b/core/res/res/layout-television/autofill_save.xml
@@ -0,0 +1,100 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 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.
+-->
+
+<!-- NOTE: outer layout is required to provide proper shadow. -->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <LinearLayout
+ android:orientation="horizontal"
+ android:id="@+id/autofill_save"
+ android:background="?android:attr/colorBackground"
+ android:layout_marginTop="32dp"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingStart="40dp"
+ android:paddingEnd="40dp"
+ android:paddingTop="40dp"
+ android:paddingBottom="40dp">
+
+ <LinearLayout
+ android:orientation="vertical"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:layout_marginEnd="32dp">
+
+ <LinearLayout
+ android:orientation="horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="6dp">
+
+ <ImageView
+ android:id="@+id/autofill_save_icon"
+ android:scaleType="fitStart"
+ android:layout_marginEnd="10dp"
+ android:layout_gravity="center_vertical"
+ android:layout_width="48dp"
+ android:layout_height="48dp"/>
+ <TextView
+ android:id="@+id/autofill_save_title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/autofill_save_title"
+ android:layout_gravity="center_vertical"
+ android:textSize="24sp" />
+ </LinearLayout>
+
+ <com.android.server.autofill.ui.CustomScrollView
+ android:id="@+id/autofill_save_custom_subtitle"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="4dp"
+ android:visibility="gone"/>
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="304dp"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <Button
+ android:id="@+id/autofill_save_no"
+ style="?attr/borderlessButtonStyle"
+ android:textAlignment="viewStart"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/autofill_save_no">
+ </Button>
+
+ <Button
+ android:id="@+id/autofill_save_yes"
+ style="?attr/borderlessButtonStyle"
+ android:textAlignment="viewStart"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/autofill_save_yes">
+ </Button>
+ </LinearLayout>
+
+ </LinearLayout>
+
+</LinearLayout>
diff --git a/core/res/res/layout/autofill_dataset_picker.xml b/core/res/res/layout/autofill_dataset_picker.xml
index ef19f87..a88836e 100644
--- a/core/res/res/layout/autofill_dataset_picker.xml
+++ b/core/res/res/layout/autofill_dataset_picker.xml
@@ -14,8 +14,7 @@
limitations under the License.
-->
-<view xmlns:android="http://schemas.android.com/apk/res/android"
- class="com.android.server.autofill.ui.FillUi$AutofillFrameLayout"
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/autofill_dataset_picker"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
@@ -31,4 +30,4 @@
android:visibility="gone">
</ListView>
-</view>
+</FrameLayout>
diff --git a/core/res/res/layout/autofill_dataset_picker_fullscreen.xml b/core/res/res/layout/autofill_dataset_picker_fullscreen.xml
index 07298c1..1d2b5e5 100644
--- a/core/res/res/layout/autofill_dataset_picker_fullscreen.xml
+++ b/core/res/res/layout/autofill_dataset_picker_fullscreen.xml
@@ -14,35 +14,73 @@
limitations under the License.
-->
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/autofill_dataset_picker"
- style="@style/AutofillDatasetPicker"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent">
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="horizontal"
+ android:background="?android:attr/windowBackground"
+ android:paddingStart="40dp"
+ android:paddingEnd="40dp"
+ android:paddingTop="40dp"
+ android:paddingBottom="40dp">
- <TextView
- android:layout_width="wrap_content"
+ <LinearLayout
+ android:orientation="vertical"
+ android:layout_width="0dp"
android:layout_height="wrap_content"
- android:text="@string/autofill_window_title"
- android:layout_above="@+id/autofill_dataset_container"
- android:layout_alignStart="@+id/autofill_dataset_container"
- android:textSize="16sp"/>
+ android:layout_weight="1"
+ android:layout_marginEnd="32dp">
+
+ <LinearLayout
+ android:orientation="horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="6dp"
+ >
+ <ImageView
+ android:id="@+id/autofill_dataset_icon"
+ android:scaleType="fitStart"
+ android:layout_marginEnd="10dp"
+ android:layout_gravity="center_vertical"
+ android:layout_width="48dp"
+ android:layout_height="48dp"/>
+ <TextView
+ android:id="@+id/autofill_dataset_title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:textSize="24sp" />
+ </LinearLayout>
+
+ <LinearLayout
+ android:id="@+id/autofill_dataset_header"
+ android:visibility="gone"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"/>
+
+ </LinearLayout>
<!-- autofill_container is the common parent for inserting authentication item or
- autofill_dataset_list-->
- <FrameLayout
- android:id="@+id/autofill_dataset_container"
- android:layout_width="wrap_content"
+ autofill_dataset_list, autofill_dataset_foolter-->
+ <LinearLayout
+ android:layout_width="304dp"
android:layout_height="wrap_content"
- android:layout_centerInParent="true">
+ android:id="@+id/autofill_dataset_picker"
+ android:orientation="vertical">
<ListView
android:id="@+id/autofill_dataset_list"
- android:layout_width="wrap_content"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clickable="true"
android:divider="@null"
android:drawSelectorOnTop="true"
android:visibility="gone"/>
- </FrameLayout>
+ <LinearLayout
+ android:id="@+id/autofill_dataset_footer"
+ android:visibility="gone"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical"/>
+ </LinearLayout>
-</RelativeLayout>
+</LinearLayout>
\ No newline at end of file
diff --git a/core/res/res/layout/autofill_dataset_picker_header_footer.xml b/core/res/res/layout/autofill_dataset_picker_header_footer.xml
index 048494a..093f035 100644
--- a/core/res/res/layout/autofill_dataset_picker_header_footer.xml
+++ b/core/res/res/layout/autofill_dataset_picker_header_footer.xml
@@ -14,8 +14,7 @@
limitations under the License.
-->
-<view xmlns:android="http://schemas.android.com/apk/res/android"
- class="com.android.server.autofill.ui.FillUi$AutofillFrameLayout"
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/autofill_dataset_picker"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
@@ -54,4 +53,4 @@
</LinearLayout>
-</view>
+</FrameLayout>
diff --git a/core/res/res/layout/autofill_dataset_picker_header_footer_fullscreen.xml b/core/res/res/layout/autofill_dataset_picker_header_footer_fullscreen.xml
deleted file mode 100644
index 24b14a0..0000000
--- a/core/res/res/layout/autofill_dataset_picker_header_footer_fullscreen.xml
+++ /dev/null
@@ -1,73 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2018 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.
--->
-
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/autofill_dataset_picker"
- style="@style/AutofillDatasetPicker"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent">
-
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:text="@string/autofill_window_title"
- android:layout_above="@+id/autofill_dataset_container"
- android:layout_alignStart="@+id/autofill_dataset_container"
- android:textSize="16sp"/>
-
- <!-- autofill_container is the common parent for inserting authentication item or
- autofill_dataset_list-->
- <FrameLayout
- android:id="@+id/autofill_dataset_container"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_centerInParent="true">
-
- <LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical">
-
- <LinearLayout
- android:id="@+id/autofill_dataset_header"
- android:visibility="gone"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"/>
-
- <ListView
- android:id="@+id/autofill_dataset_list"
- android:layout_weight="1"
- android:layout_width="fill_parent"
- android:layout_height="0dp"
- android:clickable="true"
- android:divider="@null"
- android:drawSelectorOnTop="true"
- android:visibility="gone"/>
-
- <LinearLayout
- android:id="@+id/autofill_dataset_footer"
- android:visibility="gone"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"/>
-
- </LinearLayout>
-
- </FrameLayout>
-
-</RelativeLayout>
diff --git a/core/res/res/layout/notification_template_material_big_text.xml b/core/res/res/layout/notification_template_material_big_text.xml
index 3a6f573..d1861d9 100644
--- a/core/res/res/layout/notification_template_material_big_text.xml
+++ b/core/res/res/layout/notification_template_material_big_text.xml
@@ -53,10 +53,10 @@
android:layout_marginTop="@dimen/notification_progress_margin_top"
android:layout_marginBottom="6dp"/>
<com.android.internal.widget.ImageFloatingTextView android:id="@+id/big_text"
+ style="@style/Widget.Material.Notification.Text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/notification_text_margin_top"
- android:textAppearance="@style/TextAppearance.Material.Notification"
android:singleLine="false"
android:gravity="top"
android:visibility="gone"
diff --git a/core/res/res/layout/notification_template_material_inbox.xml b/core/res/res/layout/notification_template_material_inbox.xml
index 23d8799..d6f0e1d 100644
--- a/core/res/res/layout/notification_template_material_inbox.xml
+++ b/core/res/res/layout/notification_template_material_inbox.xml
@@ -53,7 +53,7 @@
android:layout_marginTop="@dimen/notification_progress_margin_top"
android:layout_marginBottom="2dp"/>
<TextView android:id="@+id/inbox_text0"
- android:textAppearance="@style/TextAppearance.Material.Notification"
+ style="@style/Widget.Material.Notification.Text"
android:layout_width="match_parent"
android:layout_height="0dp"
android:singleLine="true"
@@ -62,7 +62,7 @@
android:layout_weight="1"
/>
<TextView android:id="@+id/inbox_text1"
- android:textAppearance="@style/TextAppearance.Material.Notification"
+ style="@style/Widget.Material.Notification.Text"
android:layout_width="match_parent"
android:layout_height="0dp"
android:singleLine="true"
@@ -71,7 +71,7 @@
android:layout_weight="1"
/>
<TextView android:id="@+id/inbox_text2"
- android:textAppearance="@style/TextAppearance.Material.Notification"
+ style="@style/Widget.Material.Notification.Text"
android:layout_width="match_parent"
android:layout_height="0dp"
android:singleLine="true"
@@ -80,7 +80,7 @@
android:layout_weight="1"
/>
<TextView android:id="@+id/inbox_text3"
- android:textAppearance="@style/TextAppearance.Material.Notification"
+ style="@style/Widget.Material.Notification.Text"
android:layout_width="match_parent"
android:layout_height="0dp"
android:singleLine="true"
@@ -89,7 +89,7 @@
android:layout_weight="1"
/>
<TextView android:id="@+id/inbox_text4"
- android:textAppearance="@style/TextAppearance.Material.Notification"
+ style="@style/Widget.Material.Notification.Text"
android:layout_width="match_parent"
android:layout_height="0dp"
android:singleLine="true"
@@ -98,7 +98,7 @@
android:layout_weight="1"
/>
<TextView android:id="@+id/inbox_text5"
- android:textAppearance="@style/TextAppearance.Material.Notification"
+ style="@style/Widget.Material.Notification.Text"
android:layout_width="match_parent"
android:layout_height="0dp"
android:singleLine="true"
@@ -107,7 +107,7 @@
android:layout_weight="1"
/>
<TextView android:id="@+id/inbox_text6"
- android:textAppearance="@style/TextAppearance.Material.Notification"
+ style="@style/Widget.Material.Notification.Text"
android:layout_width="match_parent"
android:layout_height="0dp"
android:singleLine="true"
diff --git a/core/res/res/layout/notification_template_messaging_group.xml b/core/res/res/layout/notification_template_messaging_group.xml
index d2fd467..9bdc495 100644
--- a/core/res/res/layout/notification_template_messaging_group.xml
+++ b/core/res/res/layout/notification_template_messaging_group.xml
@@ -41,6 +41,7 @@
android:id="@+id/group_message_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
+ android:layout_marginTop="@dimen/notification_text_margin_top"
android:spacing="2dp"
android:layout_weight="1"/>
</com.android.internal.widget.RemeasuringLinearLayout>
diff --git a/core/res/res/layout/notification_template_part_line1.xml b/core/res/res/layout/notification_template_part_line1.xml
index ca35de3..6459bb8 100644
--- a/core/res/res/layout/notification_template_part_line1.xml
+++ b/core/res/res/layout/notification_template_part_line1.xml
@@ -31,7 +31,7 @@
android:textAlignment="viewStart"
/>
<TextView android:id="@+id/text_line_1"
- android:textAppearance="@style/TextAppearance.Material.Notification"
+ style="@style/Widget.Material.Notification.Text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="end|bottom"
diff --git a/core/res/res/layout/notification_template_text.xml b/core/res/res/layout/notification_template_text.xml
index 68f34c8..3b27666 100644
--- a/core/res/res/layout/notification_template_text.xml
+++ b/core/res/res/layout/notification_template_text.xml
@@ -15,6 +15,7 @@
~ limitations under the License
-->
<com.android.internal.widget.ImageFloatingTextView xmlns:android="http://schemas.android.com/apk/res/android"
+ style="@style/Widget.Material.Notification.Text"
android:id="@+id/text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -25,5 +26,4 @@
android:gravity="top"
android:singleLine="true"
android:textAlignment="viewStart"
- android:textAppearance="@style/TextAppearance.Material.Notification"
/>
diff --git a/core/res/res/values-television/dimens.xml b/core/res/res/values-television/dimens.xml
index aa56251..4c25225 100644
--- a/core/res/res/values-television/dimens.xml
+++ b/core/res/res/values-television/dimens.xml
@@ -20,8 +20,4 @@
<item type="dimen" format="float" name="ambient_shadow_alpha">0.15</item>
<item type="dimen" format="float" name="spot_shadow_alpha">0.3</item>
- <!-- Max width/height of the autofill data set picker as a fraction of the screen width/height -->
- <dimen name="autofill_dataset_picker_max_width">60%</dimen>
- <dimen name="autofill_dataset_picker_max_height">70%</dimen>
-
</resources>
diff --git a/core/res/res/values-television/themes_device_defaults.xml b/core/res/res/values-television/themes_device_defaults.xml
index e01caa3..e380a7b 100644
--- a/core/res/res/values-television/themes_device_defaults.xml
+++ b/core/res/res/values-television/themes_device_defaults.xml
@@ -16,4 +16,6 @@
<resources>
<style name="Theme.DeviceDefault.Dialog.Alert" parent="Theme.Leanback.Dialog.Alert" />
<style name="Theme.DeviceDefault.Light.Dialog.Alert" parent="Theme.Leanback.Light.Dialog.Alert" />
+ <style name="Theme.DeviceDefault.Autofill" parent="Theme.Material.Autofill" />
+ <style name="Theme.DeviceDefault.Autofill.Save" parent="Theme.Material.Autofill.Save" />
</resources>
diff --git a/core/res/res/values/colors_material.xml b/core/res/res/values/colors_material.xml
index 3609fb8..2966aff 100644
--- a/core/res/res/values/colors_material.xml
+++ b/core/res/res/values/colors_material.xml
@@ -145,4 +145,8 @@
<color name="datepicker_default_view_animator_color_material_light">#fff2f2f2</color>
<color name="datepicker_default_view_animator_color_material_dark">#ff303030</color>
+ <!-- Autofill colors -->
+ <color name="autofill_background_material_dark">@color/material_blue_grey_900</color>
+ <color name="autofill_background_material_light">@color/material_grey_50</color>
+
</resources>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 7b90444..54864f3 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2853,6 +2853,9 @@
<!-- For performance and storage reasons, limit the number of fingerprints per user -->
<integer name="config_fingerprintMaxTemplatesPerUser">5</integer>
+ <!-- Specify if the fingerprint hardware support gestures-->
+ <bool name="config_fingerprintSupportsGestures">false</bool>
+
<!-- This config is used to force VoiceInteractionService to start on certain low ram devices.
It declares the package name of VoiceInteractionService that should be started. -->
<string translatable="false" name="config_forceVoiceInteractionServicePackage"></string>
@@ -3178,6 +3181,9 @@
<!-- An array of packages for which notifications cannot be blocked. -->
<string-array translatable="false" name="config_nonBlockableNotificationPackages" />
+ <!-- An array of packages which can listen for notifications on low ram devices. -->
+ <string-array translatable="false" name="config_allowedManagedServicesOnLowRamDevices" />
+
<!-- The default value for transition animation scale found in developer settings.
1.0 corresponds to 1x animator scale, 0 means that there will be no transition
animations. Note that this is only a default and will be overridden by a
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 84f23a9..45814ea 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -531,7 +531,7 @@
<dimen name="floating_toolbar_menu_image_width">24dp</dimen>
<dimen name="floating_toolbar_menu_image_button_width">56dp</dimen>
<dimen name="floating_toolbar_menu_image_button_vertical_padding">12dp</dimen>
- <dimen name="floating_toolbar_menu_button_side_padding">8dp</dimen>
+ <dimen name="floating_toolbar_menu_button_side_padding">11dp</dimen>
<dimen name="floating_toolbar_overflow_image_button_width">60dp</dimen>
<dimen name="floating_toolbar_overflow_side_padding">18dp</dimen>
<dimen name="floating_toolbar_text_size">14sp</dimen>
@@ -542,6 +542,7 @@
<dimen name="floating_toolbar_horizontal_margin">16dp</dimen>
<dimen name="floating_toolbar_vertical_margin">8dp</dimen>
<dimen name="content_rect_bottom_clip_allowance">20dp</dimen>
+ <dimen name="floating_toolbar_icon_text_spacing">8dp</dimen>
<!-- Magnifier dimensions -->
<dimen name="magnifier_width">100dp</dimen>
@@ -628,7 +629,7 @@
<!-- The maximum width of a image in a media notification. The images will be reduced to that width in case they are bigger.-->
<dimen name="notification_media_image_max_width_low_ram">100dp</dimen>
<!-- The size of the right icon image when on low ram -->
- <dimen name="notification_right_icon_size_low_ram">@dimen/notification_right_icon_size_low_ram</dimen>
+ <dimen name="notification_right_icon_size_low_ram">38dp</dimen>
<dimen name="messaging_avatar_size">@dimen/notification_right_icon_size</dimen>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 395b269..b92052b 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -2196,8 +2196,8 @@
<!-- Text to show in the auto complete drop down list on a text view when the WebView can auto fill the entire form but the user has not configured an AutoFill profile [CHAR-LIMIT=19] -->
<string name="setup_autofill">Set up Autofill</string>
- <!-- Title of fullscreen autofill window [CHAR-LIMIT=80] -->
- <string name="autofill_window_title">Autofill</string>
+ <!-- Title of fullscreen autofill window, including the name of which autofill service it is using [CHAR-LIMIT=NONE] -->
+ <string name="autofill_window_title">Autofill with <xliff:g id="serviceName" example="MyPass">%1$s</xliff:g></string>
<!-- String used to separate FirstName and LastName when writing out a local name
e.g. John<separator>Smith [CHAR-LIMIT=NONE]-->
@@ -4514,7 +4514,10 @@
<!-- Notification shown when device owner silently deletes a package [CHAR LIMIT=NONE] -->
<string name="package_deleted_device_owner">Deleted by your admin</string>
- <!-- [CHAR_LIMIT=NONE] Battery saver: Feature description -->
+ <!-- [CHAR_LIMIT=NONE] Battery saver: Feature description, with a "learn more" link. -->
+ <string name="battery_saver_description_with_learn_more">To extend your battery life, Battery Saver turns off some device features and restricts apps. <annotation id="url">Learn More</annotation></string>
+
+ <!-- [CHAR_LIMIT=NONE] Battery saver: Feature description, without a "learn more" link. -->
<string name="battery_saver_description">To extend your battery life, Battery Saver turns off some device features and restricts apps.</string>
<!-- [CHAR_LIMIT=NONE] Data saver: Feature description -->
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index 984461b..50a6ff3 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -1487,13 +1487,19 @@
<item name="successColor">@color/lock_pattern_view_success_color</item>
</style>
- <!-- @hide -->
+ <!-- @hide Autofill background for popup window (not for fullscreen) -->
<style name="AutofillDatasetPicker">
<item name="elevation">4dp</item>
<item name="background">@drawable/autofill_dataset_picker_background</item>
</style>
<!-- @hide -->
+ <style name="AutofillHalfScreenAnimation">
+ <item name="android:windowEnterAnimation">@anim/slide_in_up</item>
+ <item name="android:windowExitAnimation">@anim/slide_out_down</item>
+ </style>
+
+ <!-- @hide -->
<style name="AutofillSaveAnimation">
<item name="android:windowEnterAnimation">@anim/slide_in_up</item>
<item name="android:windowExitAnimation">@anim/slide_out_down</item>
diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml
index b8a046f..2d97db0 100644
--- a/core/res/res/values/styles_material.xml
+++ b/core/res/res/values/styles_material.xml
@@ -500,11 +500,15 @@
<style name="Widget.Material.Notification.ProgressBar" parent="Widget.Material.Light.ProgressBar.Horizontal" />
- <style name="Widget.Material.Notification.MessagingText" parent="Widget.Material.Light.TextView">
+ <style name="Widget.Material.Notification.Text" parent="Widget.Material.Light.TextView">
+ <item name="lineHeight">20sp</item>
+ <item name="textAppearance">@style/TextAppearance.Material.Notification</item>
+ </style>
+
+ <style name="Widget.Material.Notification.MessagingText" parent="Widget.Material.Notification.Text">
<item name="layout_width">wrap_content</item>
<item name="layout_height">wrap_content</item>
- <item name="ellipsize">end</item>
- <item name="textAppearance">@style/TextAppearance.Material.Notification</item>
+ <item name="ellipsize">end</item>z
</style>
<style name="Widget.Material.Notification.MessagingName" parent="Widget.Material.Light.TextView">
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 6721f93..d7a1ecc 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -663,6 +663,7 @@
<java-symbol type="string" name="autofill_state_re" />
<java-symbol type="string" name="autofill_this_form" />
<java-symbol type="string" name="autofill_username_re" />
+ <java-symbol type="string" name="autofill_window_title" />
<java-symbol type="string" name="autofill_zip_4_re" />
<java-symbol type="string" name="autofill_zip_code" />
<java-symbol type="string" name="autofill_zip_code_re" />
@@ -2407,6 +2408,7 @@
<!-- Fingerprint config -->
<java-symbol type="integer" name="config_fingerprintMaxTemplatesPerUser"/>
+ <java-symbol type="bool" name="config_fingerprintSupportsGestures"/>
<!-- From various Material changes -->
<java-symbol type="attr" name="titleTextAppearance" />
@@ -2575,6 +2577,7 @@
<java-symbol type="dimen" name="floating_toolbar_maximum_overflow_height" />
<java-symbol type="dimen" name="floating_toolbar_horizontal_margin" />
<java-symbol type="dimen" name="floating_toolbar_vertical_margin" />
+ <java-symbol type="dimen" name="floating_toolbar_icon_text_spacing" />
<java-symbol type="dimen" name="content_rect_bottom_clip_allowance" />
<java-symbol type="drawable" name="ft_avd_tooverflow" />
<java-symbol type="drawable" name="ft_avd_toarrow" />
@@ -2977,6 +2980,8 @@
<java-symbol type="array" name="config_nonBlockableNotificationPackages" />
+ <java-symbol type="array" name="config_allowedManagedServicesOnLowRamDevices" />
+
<!-- Screen-size-dependent modes for picker dialogs. -->
<java-symbol type="integer" name="time_picker_mode" />
<java-symbol type="integer" name="date_picker_mode" />
@@ -3051,13 +3056,13 @@
<java-symbol type="layout" name="autofill_dataset_picker"/>
<java-symbol type="layout" name="autofill_dataset_picker_fullscreen"/>
<java-symbol type="layout" name="autofill_dataset_picker_header_footer"/>
- <java-symbol type="layout" name="autofill_dataset_picker_header_footer_fullscreen"/>
<java-symbol type="id" name="autofill" />
- <java-symbol type="id" name="autofill_dataset_container"/>
<java-symbol type="id" name="autofill_dataset_footer"/>
<java-symbol type="id" name="autofill_dataset_header"/>
+ <java-symbol type="id" name="autofill_dataset_icon" />
<java-symbol type="id" name="autofill_dataset_list"/>
<java-symbol type="id" name="autofill_dataset_picker"/>
+ <java-symbol type="id" name="autofill_dataset_title" />
<java-symbol type="id" name="autofill_save_custom_subtitle" />
<java-symbol type="id" name="autofill_save_icon" />
<java-symbol type="id" name="autofill_save_no" />
@@ -3082,6 +3087,7 @@
<java-symbol type="string" name="autofill_save_type_email_address" />
<java-symbol type="drawable" name="autofill_dataset_picker_background" />
<java-symbol type="style" name="AutofillDatasetPicker" />
+ <java-symbol type="style" name="AutofillHalfScreenAnimation" />
<java-symbol type="style" name="AutofillSaveAnimation" />
<java-symbol type="dimen" name="autofill_dataset_picker_max_width"/>
<java-symbol type="dimen" name="autofill_dataset_picker_max_height"/>
@@ -3089,6 +3095,9 @@
<java-symbol type="dimen" name="autofill_save_icon_max_size"/>
<java-symbol type="integer" name="autofill_max_visible_datasets" />
+ <java-symbol type="style" name="Theme.DeviceDefault.Autofill" />
+ <java-symbol type="style" name="Theme.DeviceDefault.Autofill.Save" />
+
<java-symbol type="dimen" name="notification_big_picture_max_height"/>
<java-symbol type="dimen" name="notification_big_picture_max_width"/>
<java-symbol type="dimen" name="notification_media_image_max_width"/>
@@ -3369,4 +3378,5 @@
<java-symbol type="id" name="user_loading_avatar" />
<java-symbol type="id" name="user_loading" />
+ <java-symbol type="string" name="battery_saver_description_with_learn_more" />
</resources>
diff --git a/core/res/res/values/themes_device_defaults.xml b/core/res/res/values/themes_device_defaults.xml
index 92b2f33..14e5082 100644
--- a/core/res/res/values/themes_device_defaults.xml
+++ b/core/res/res/values/themes_device_defaults.xml
@@ -1673,6 +1673,15 @@
<item name="toolbarStyle">@style/Widget.DeviceDefault.Toolbar</item>
</style>
+
+ <!-- @hide DeviceDefault theme for the autofill FillUi -->
+ <style name="Theme.DeviceDefault.Autofill" parent="Theme.Material.Autofill.Light">
+ </style>
+
+ <!-- @hide DeviceDefault theme for the autofill SaveUi -->
+ <style name="Theme.DeviceDefault.Autofill.Save" parent="Theme.Material.Autofill.Save.Light">
+ </style>
+
<!-- DeviceDefault theme for the default system theme. -->
<style name="Theme.DeviceDefault.System" parent="Theme.DeviceDefault.Light.DarkActionBar" />
diff --git a/core/res/res/values/themes_material.xml b/core/res/res/values/themes_material.xml
index 9b633fc..b3e33d5 100644
--- a/core/res/res/values/themes_material.xml
+++ b/core/res/res/values/themes_material.xml
@@ -1417,4 +1417,25 @@
<item name="colorPrimaryDark">@color/primary_dark_material_settings</item>
<item name="colorSecondary">@color/secondary_material_settings</item>
</style>
+
+ <!-- @hide -->
+ <style name="Theme.Material.Autofill" parent="Theme.Material">
+ <item name="colorBackground">@color/autofill_background_material_dark</item>
+ </style>
+
+ <!-- @hide -->
+ <style name="Theme.Material.Autofill.Light" parent="Theme.Material.Light">
+ <item name="colorBackground">@color/autofill_background_material_light</item>
+ </style>
+
+ <!-- @hide -->
+ <style name="Theme.Material.Autofill.Save" parent="Theme.Material.Panel">
+ <item name="colorBackground">@color/autofill_background_material_dark</item>
+ </style>
+
+ <!-- @hide -->
+ <style name="Theme.Material.Autofill.Save.Light" parent="Theme.Material.Light.Panel">
+ <item name="colorBackground">@color/autofill_background_material_light</item>
+ </style>
+
</resources>
diff --git a/core/tests/coretests/src/android/app/usage/EventListTest.java b/core/tests/coretests/src/android/app/usage/EventListTest.java
new file mode 100644
index 0000000..9dc0d43
--- /dev/null
+++ b/core/tests/coretests/src/android/app/usage/EventListTest.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.app.usage;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.Log;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Random;
+
+@RunWith(AndroidJUnit4.class)
+@SmallTest
+public class EventListTest {
+ private static final String TAG = EventListTest.class.getSimpleName();
+
+ private UsageEvents.Event getUsageEvent(long timeStamp) {
+ final UsageEvents.Event event = new UsageEvents.Event();
+ event.mTimeStamp = timeStamp;
+ return event;
+ }
+
+ private static String getListTimeStamps(EventList list) {
+ final StringBuilder builder = new StringBuilder("[");
+ for (int i = 0; i < list.size() - 1; i++) {
+ builder.append(list.get(i).mTimeStamp);
+ builder.append(", ");
+ }
+ builder.append(list.get(list.size() - 1).mTimeStamp);
+ builder.append("]");
+ return builder.toString();
+ }
+
+ private static void assertSorted(EventList eventList) {
+ for (int i = 1; i < eventList.size(); i++) {
+ final long lastTimeStamp = eventList.get(i - 1).mTimeStamp;
+ if (eventList.get(i).mTimeStamp < lastTimeStamp) {
+ Log.e(TAG, "Unsorted timestamps in list: " + getListTimeStamps(eventList));
+ fail("Timestamp " + eventList.get(i).mTimeStamp + " at " + i
+ + " follows larger timestamp " + lastTimeStamp);
+ }
+ }
+ }
+
+ @Test
+ public void testInsertsSortedRandom() {
+ final Random random = new Random(128);
+ final EventList listUnderTest = new EventList();
+ for (int i = 0; i < 100; i++) {
+ listUnderTest.insert(getUsageEvent(random.nextLong()));
+ }
+ assertSorted(listUnderTest);
+ }
+
+ @Test
+ public void testInsertsSortedWithDuplicates() {
+ final Random random = new Random(256);
+ final EventList listUnderTest = new EventList();
+ for (int i = 0; i < 10; i++) {
+ final long randomTimeStamp = random.nextLong();
+ for (int j = 0; j < 10; j++) {
+ listUnderTest.insert(getUsageEvent(randomTimeStamp));
+ }
+ }
+ assertSorted(listUnderTest);
+ }
+
+ @Test
+ public void testFirstIndexOnOrAfter() {
+ final EventList listUnderTest = new EventList();
+ listUnderTest.insert(getUsageEvent(2));
+ listUnderTest.insert(getUsageEvent(5));
+ listUnderTest.insert(getUsageEvent(5));
+ listUnderTest.insert(getUsageEvent(5));
+ listUnderTest.insert(getUsageEvent(8));
+ assertTrue(listUnderTest.firstIndexOnOrAfter(1) == 0);
+ assertTrue(listUnderTest.firstIndexOnOrAfter(2) == 0);
+ assertTrue(listUnderTest.firstIndexOnOrAfter(3) == 1);
+ assertTrue(listUnderTest.firstIndexOnOrAfter(4) == 1);
+ assertTrue(listUnderTest.firstIndexOnOrAfter(5) == 1);
+ assertTrue(listUnderTest.firstIndexOnOrAfter(6) == 4);
+ assertTrue(listUnderTest.firstIndexOnOrAfter(7) == 4);
+ assertTrue(listUnderTest.firstIndexOnOrAfter(8) == 4);
+ assertTrue(listUnderTest.firstIndexOnOrAfter(9) == listUnderTest.size());
+ assertTrue(listUnderTest.firstIndexOnOrAfter(100) == listUnderTest.size());
+
+ listUnderTest.clear();
+ assertTrue(listUnderTest.firstIndexOnOrAfter(5) == 0);
+ assertTrue(listUnderTest.firstIndexOnOrAfter(100) == 0);
+ }
+
+ @Test
+ public void testClear() {
+ final EventList listUnderTest = new EventList();
+ for (int i = 1; i <= 100; i++) {
+ listUnderTest.insert(getUsageEvent(i));
+ }
+ listUnderTest.clear();
+ assertEquals(0, listUnderTest.size());
+ }
+
+ @Test
+ public void testSize() {
+ final EventList listUnderTest = new EventList();
+ for (int i = 1; i <= 100; i++) {
+ listUnderTest.insert(getUsageEvent(i));
+ }
+ assertEquals(100, listUnderTest.size());
+ }
+}
diff --git a/core/tests/coretests/src/android/app/usage/TimeSparseArrayTest.java b/core/tests/coretests/src/android/app/usage/TimeSparseArrayTest.java
deleted file mode 100644
index db46740..0000000
--- a/core/tests/coretests/src/android/app/usage/TimeSparseArrayTest.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-
-package android.app.usage;
-
-import static org.junit.Assert.assertTrue;
-
-import android.os.SystemClock;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
-
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-@RunWith(AndroidJUnit4.class)
-@SmallTest
-public class TimeSparseArrayTest {
- @Test
- public void testDuplicateKeysNotDropped() {
- final TimeSparseArray<Integer> testTimeSparseArray = new TimeSparseArray<>();
- final long key = SystemClock.elapsedRealtime();
- for (int i = 0; i < 5; i++) {
- testTimeSparseArray.put(key, i);
- }
- for (int i = 0; i < 5; i++) {
- final int valueIndex = testTimeSparseArray.indexOfValue(i);
- assertTrue("Value " + i + " not found; intended key: " + key , valueIndex >= 0);
- final long keyForValue = testTimeSparseArray.keyAt(valueIndex);
- assertTrue("Value " + i + " stored too far (at " + keyForValue + ") from intended key "
- + key, Math.abs(keyForValue - key) < 100);
- }
- }
-}
diff --git a/core/tests/coretests/src/android/provider/SettingsBackupTest.java b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
index 43e980e..dafd475 100644
--- a/core/tests/coretests/src/android/provider/SettingsBackupTest.java
+++ b/core/tests/coretests/src/android/provider/SettingsBackupTest.java
@@ -98,6 +98,7 @@
private static final Set<String> BACKUP_BLACKLISTED_GLOBAL_SETTINGS =
newHashSet(
Settings.Global.ACTIVITY_MANAGER_CONSTANTS,
+ Settings.Global.ADAPTIVE_BATTERY_MANAGEMENT_ENABLED,
Settings.Global.ADB_ENABLED,
Settings.Global.ADD_USERS_WHEN_LOCKED,
Settings.Global.AIRPLANE_MODE_ON,
diff --git a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
index 36e54ad..b68f6b1 100644
--- a/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
+++ b/core/tests/coretests/src/com/android/internal/os/MockBatteryStatsImpl.java
@@ -190,6 +190,11 @@
@Override
public void cancelCpuSyncDueToWakelockChange() {
}
+
+ @Override
+ public Future<?> scheduleSyncDueToBatteryLevelChange(long delayMillis) {
+ return null;
+ }
}
}
diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java
index 3cc92bc..a465eea 100644
--- a/graphics/java/android/graphics/Canvas.java
+++ b/graphics/java/android/graphics/Canvas.java
@@ -829,6 +829,17 @@
}
/**
+ * DON'T USE THIS METHOD. It exists only to support a particular legacy behavior in
+ * the view system and will be removed as soon as that code is refactored to no longer
+ * depend on this behavior.
+ * @hide
+ */
+ public boolean clipRectUnion(@NonNull Rect rect) {
+ return nClipRect(mNativeCanvasWrapper, rect.left, rect.top, rect.right, rect.bottom,
+ Region.Op.UNION.nativeInt);
+ }
+
+ /**
* Intersect the current clip with the specified rectangle, which is
* expressed in local coordinates.
*
diff --git a/libs/androidfw/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp
index 7d4e6f8..b3a3f45 100644
--- a/libs/androidfw/ResourceTypes.cpp
+++ b/libs/androidfw/ResourceTypes.cpp
@@ -3504,13 +3504,14 @@
{
PackageGroup(
ResTable* _owner, const String16& _name, uint32_t _id,
- bool appAsLib, bool _isSystemAsset)
+ bool appAsLib, bool _isSystemAsset, bool _isDynamic)
: owner(_owner)
, name(_name)
, id(_id)
, largestTypeId(0)
, dynamicRefTable(static_cast<uint8_t>(_id), appAsLib)
, isSystemAsset(_isSystemAsset)
+ , isDynamic(_isDynamic)
{ }
~PackageGroup() {
@@ -3614,6 +3615,7 @@
// If the package group comes from a system asset. Used in
// determining non-system locales.
const bool isSystemAsset;
+ const bool isDynamic;
};
ResTable::Theme::Theme(const ResTable& table)
@@ -3982,6 +3984,11 @@
return ((ssize_t)mPackageMap[Res_GETPACKAGE(resID)+1])-1;
}
+inline ssize_t ResTable::getResourcePackageIndexFromPackage(uint8_t packageID) const
+{
+ return ((ssize_t)mPackageMap[packageID])-1;
+}
+
status_t ResTable::add(const void* data, size_t size, const int32_t cookie, bool copyData) {
return addInternal(data, size, NULL, 0, false, cookie, copyData);
}
@@ -4037,7 +4044,7 @@
for (size_t i=0; i < src->mPackageGroups.size(); i++) {
PackageGroup* srcPg = src->mPackageGroups[i];
PackageGroup* pg = new PackageGroup(this, srcPg->name, srcPg->id,
- false /* appAsLib */, isSystemAsset || srcPg->isSystemAsset);
+ false /* appAsLib */, isSystemAsset || srcPg->isSystemAsset, srcPg->isDynamic);
for (size_t j=0; j<srcPg->packages.size(); j++) {
pg->packages.add(srcPg->packages[j]);
}
@@ -6277,6 +6284,68 @@
return true;
}
+bool ResTable::isPackageDynamic(uint8_t packageID) const {
+ if (mError != NO_ERROR) {
+ return false;
+ }
+ if (packageID == 0) {
+ ALOGW("Invalid package number 0x%08x", packageID);
+ return false;
+ }
+
+ const ssize_t p = getResourcePackageIndexFromPackage(packageID);
+
+ if (p < 0) {
+ ALOGW("Unknown package number 0x%08x", packageID);
+ return false;
+ }
+
+ const PackageGroup* const grp = mPackageGroups[p];
+ if (grp == NULL) {
+ ALOGW("Bad identifier for package number 0x%08x", packageID);
+ return false;
+ }
+
+ return grp->isDynamic;
+}
+
+bool ResTable::isResourceDynamic(uint32_t resID) const {
+ if (mError != NO_ERROR) {
+ return false;
+ }
+
+ const ssize_t p = getResourcePackageIndex(resID);
+ const int t = Res_GETTYPE(resID);
+ const int e = Res_GETENTRY(resID);
+
+ if (p < 0) {
+ if (Res_GETPACKAGE(resID)+1 == 0) {
+ ALOGW("No package identifier for resource number 0x%08x", resID);
+ } else {
+ ALOGW("No known package for resource number 0x%08x", resID);
+ }
+ return false;
+ }
+ if (t < 0) {
+ ALOGW("No type identifier for resource number 0x%08x", resID);
+ return false;
+ }
+
+ const PackageGroup* const grp = mPackageGroups[p];
+ if (grp == NULL) {
+ ALOGW("Bad identifier for resource number 0x%08x", resID);
+ return false;
+ }
+
+ Entry entry;
+ status_t err = getEntry(grp, t, e, NULL, &entry);
+ if (err != NO_ERROR) {
+ return false;
+ }
+
+ return grp->isDynamic;
+}
+
static bool keyCompare(const ResTable_sparseTypeEntry& entry , uint16_t entryIdx) {
return dtohs(entry.idx) < entryIdx;
}
@@ -6520,12 +6589,14 @@
id = targetPackageId;
}
+ bool isDynamic = false;
if (id >= 256) {
LOG_ALWAYS_FATAL("Package id out of range");
return NO_ERROR;
} else if (id == 0 || (id == 0x7f && appAsLib) || isSystemAsset) {
// This is a library or a system asset, so assign an ID
id = mNextPackageId++;
+ isDynamic = true;
}
PackageGroup* group = NULL;
@@ -6553,10 +6624,9 @@
size_t idx = mPackageMap[id];
if (idx == 0) {
idx = mPackageGroups.size() + 1;
-
char16_t tmpName[sizeof(pkg->name)/sizeof(pkg->name[0])];
strcpy16_dtoh(tmpName, pkg->name, sizeof(pkg->name)/sizeof(pkg->name[0]));
- group = new PackageGroup(this, String16(tmpName), id, appAsLib, isSystemAsset);
+ group = new PackageGroup(this, String16(tmpName), id, appAsLib, isSystemAsset, isDynamic);
if (group == NULL) {
delete package;
return (mError=NO_MEMORY);
diff --git a/libs/androidfw/include/androidfw/ResourceTypes.h b/libs/androidfw/include/androidfw/ResourceTypes.h
index bc0a07a..63b9e3a 100644
--- a/libs/androidfw/include/androidfw/ResourceTypes.h
+++ b/libs/androidfw/include/androidfw/ResourceTypes.h
@@ -1716,6 +1716,18 @@
bool getResourceFlags(uint32_t resID, uint32_t* outFlags) const;
/**
+ * Returns whether or not the package for the given resource has been dynamically assigned.
+ * If the resource can't be found, returns 'false'.
+ */
+ bool isResourceDynamic(uint32_t resID) const;
+
+ /**
+ * Returns whether or not the given package has been dynamically assigned.
+ * If the package can't be found, returns 'false'.
+ */
+ bool isPackageDynamic(uint8_t packageID) const;
+
+ /**
* Retrieve the value of a resource. If the resource is found, returns a
* value >= 0 indicating the table it is in (for use with
* getTableStringBlock() and getTableCookie()) and fills in 'outValue'. If
@@ -2024,6 +2036,7 @@
bool appAsLib, const int32_t cookie, bool copyData, bool isSystemAsset=false);
ssize_t getResourcePackageIndex(uint32_t resID) const;
+ ssize_t getResourcePackageIndexFromPackage(uint8_t packageID) const;
status_t getEntry(
const PackageGroup* packageGroup, int typeIndex, int entryIndex,
diff --git a/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java b/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
index 0e2a0e0..91c04fa 100644
--- a/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
+++ b/packages/CaptivePortalLogin/src/com/android/captiveportallogin/CaptivePortalLoginActivity.java
@@ -321,6 +321,7 @@
// TODO: reuse NetworkMonitor facilities for consistent captive portal detection.
new Thread(new Runnable() {
public void run() {
+ final Network network = ResolvUtil.makeNetworkWithPrivateDnsBypass(mNetwork);
// Give time for captive portal to open.
try {
Thread.sleep(1000);
@@ -329,7 +330,7 @@
HttpURLConnection urlConnection = null;
int httpResponseCode = 500;
try {
- urlConnection = (HttpURLConnection) mNetwork.openConnection(mUrl);
+ urlConnection = (HttpURLConnection) network.openConnection(mUrl);
urlConnection.setInstanceFollowRedirects(false);
urlConnection.setConnectTimeout(SOCKET_TIMEOUT_MS);
urlConnection.setReadTimeout(SOCKET_TIMEOUT_MS);
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtils.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtils.java
index aeb4a85..8bab3ca 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedLockUtils.java
@@ -46,6 +46,7 @@
import com.android.internal.widget.LockPatternUtils;
import java.util.List;
+import java.util.Objects;
/**
* Utility class to host methods usable in adding a restricted padlock icon and showing admin
@@ -90,29 +91,29 @@
// Restriction is not enforced.
return null;
} else if (enforcingUsers.size() > 1) {
- return EnforcedAdmin.MULTIPLE_ENFORCED_ADMIN;
+ return EnforcedAdmin.createDefaultEnforcedAdminWithRestriction(userRestriction);
}
final int restrictionSource = enforcingUsers.get(0).getUserRestrictionSource();
final int adminUserId = enforcingUsers.get(0).getUserHandle().getIdentifier();
-
if (restrictionSource == UserManager.RESTRICTION_SOURCE_PROFILE_OWNER) {
// Check if it is a profile owner of the user under consideration.
if (adminUserId == userId) {
- return getProfileOwner(context, adminUserId);
+ return getProfileOwner(context, userRestriction, adminUserId);
} else {
// Check if it is a profile owner of a managed profile of the current user.
// Otherwise it is in a separate user and we return a default EnforcedAdmin.
final UserInfo parentUser = um.getProfileParent(adminUserId);
return (parentUser != null && parentUser.id == userId)
- ? getProfileOwner(context, adminUserId)
- : EnforcedAdmin.MULTIPLE_ENFORCED_ADMIN;
+ ? getProfileOwner(context, userRestriction, adminUserId)
+ : EnforcedAdmin.createDefaultEnforcedAdminWithRestriction(userRestriction);
}
} else if (restrictionSource == UserManager.RESTRICTION_SOURCE_DEVICE_OWNER) {
// When the restriction is enforced by device owner, return the device owner admin only
// if the admin is for the {@param userId} otherwise return a default EnforcedAdmin.
return adminUserId == userId
- ? getDeviceOwner(context) : EnforcedAdmin.MULTIPLE_ENFORCED_ADMIN;
+ ? getDeviceOwner(context, userRestriction)
+ : EnforcedAdmin.createDefaultEnforcedAdminWithRestriction(userRestriction);
}
// If the restriction is enforced by system then return null.
@@ -406,7 +407,6 @@
* or {@code null} if no quality requirements are set. If the requirements are set by
* multiple device admins, then the admin component will be set to {@code null} and userId to
* {@link UserHandle#USER_NULL}.
- *
*/
public static EnforcedAdmin checkIfPasswordQualityIsSet(Context context, int userId) {
final LockSettingCheck check =
@@ -518,6 +518,11 @@
}
public static EnforcedAdmin getProfileOrDeviceOwner(Context context, int userId) {
+ return getProfileOrDeviceOwner(context, null, userId);
+ }
+
+ public static EnforcedAdmin getProfileOrDeviceOwner(
+ Context context, String enforcedRestriction, int userId) {
if (userId == UserHandle.USER_NULL) {
return null;
}
@@ -528,18 +533,22 @@
}
ComponentName adminComponent = dpm.getProfileOwnerAsUser(userId);
if (adminComponent != null) {
- return new EnforcedAdmin(adminComponent, userId);
+ return new EnforcedAdmin(adminComponent, enforcedRestriction, userId);
}
if (dpm.getDeviceOwnerUserId() == userId) {
adminComponent = dpm.getDeviceOwnerComponentOnAnyUser();
if (adminComponent != null) {
- return new EnforcedAdmin(adminComponent, userId);
+ return new EnforcedAdmin(adminComponent, enforcedRestriction, userId);
}
}
return null;
}
public static EnforcedAdmin getDeviceOwner(Context context) {
+ return getDeviceOwner(context, null);
+ }
+
+ private static EnforcedAdmin getDeviceOwner(Context context, String enforcedRestriction) {
final DevicePolicyManager dpm = (DevicePolicyManager) context.getSystemService(
Context.DEVICE_POLICY_SERVICE);
if (dpm == null) {
@@ -547,12 +556,18 @@
}
ComponentName adminComponent = dpm.getDeviceOwnerComponentOnAnyUser();
if (adminComponent != null) {
- return new EnforcedAdmin(adminComponent, dpm.getDeviceOwnerUserId());
+ return new EnforcedAdmin(
+ adminComponent, enforcedRestriction, dpm.getDeviceOwnerUserId());
}
return null;
}
private static EnforcedAdmin getProfileOwner(Context context, int userId) {
+ return getProfileOwner(context, null, userId);
+ }
+
+ private static EnforcedAdmin getProfileOwner(
+ Context context, String enforcedRestriction, int userId) {
if (userId == UserHandle.USER_NULL) {
return null;
}
@@ -563,7 +578,7 @@
}
ComponentName adminComponent = dpm.getProfileOwnerAsUser(userId);
if (adminComponent != null) {
- return new EnforcedAdmin(adminComponent, userId);
+ return new EnforcedAdmin(adminComponent, enforcedRestriction, userId);
}
return null;
}
@@ -626,6 +641,7 @@
&& isCurrentUserOrProfile(context, admin.userId)) {
targetUserId = admin.userId;
}
+ intent.putExtra(DevicePolicyManager.EXTRA_RESTRICTION, admin.enforcedRestriction);
context.startActivityAsUser(intent, new UserHandle(targetUserId));
}
@@ -700,53 +716,71 @@
}
public static class EnforcedAdmin {
+ @Nullable
public ComponentName component = null;
+ /**
+ * The restriction enforced by admin. It could be any user restriction or policy like
+ * {@link DevicePolicyManager#POLICY_DISABLE_CAMERA}.
+ */
+ @Nullable
+ public String enforcedRestriction = null;
public int userId = UserHandle.USER_NULL;
// We use this to represent the case where a policy is enforced by multiple admins.
public final static EnforcedAdmin MULTIPLE_ENFORCED_ADMIN = new EnforcedAdmin();
+ public static EnforcedAdmin createDefaultEnforcedAdminWithRestriction(
+ String enforcedRestriction) {
+ EnforcedAdmin enforcedAdmin = new EnforcedAdmin();
+ enforcedAdmin.enforcedRestriction = enforcedRestriction;
+ return enforcedAdmin;
+ }
+
public EnforcedAdmin(ComponentName component, int userId) {
this.component = component;
this.userId = userId;
}
+ public EnforcedAdmin(ComponentName component, String enforcedRestriction, int userId) {
+ this.component = component;
+ this.enforcedRestriction = enforcedRestriction;
+ this.userId = userId;
+ }
+
public EnforcedAdmin(EnforcedAdmin other) {
if (other == null) {
throw new IllegalArgumentException();
}
this.component = other.component;
+ this.enforcedRestriction = other.enforcedRestriction;
this.userId = other.userId;
}
- public EnforcedAdmin() {}
+ public EnforcedAdmin() {
+ }
@Override
- public boolean equals(Object object) {
- if (object == this) return true;
- if (!(object instanceof EnforcedAdmin)) return false;
- EnforcedAdmin other = (EnforcedAdmin) object;
- if (userId != other.userId) {
- return false;
- }
- if ((component == null && other.component == null) ||
- (component != null && component.equals(other.component))) {
- return true;
- }
- return false;
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ EnforcedAdmin that = (EnforcedAdmin) o;
+ return userId == that.userId &&
+ Objects.equals(component, that.component) &&
+ Objects.equals(enforcedRestriction, that.enforcedRestriction);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(component, enforcedRestriction, userId);
}
@Override
public String toString() {
- return "EnforcedAdmin{component=" + component + ",userId=" + userId + "}";
- }
-
- public void copyTo(EnforcedAdmin other) {
- if (other == null) {
- throw new IllegalArgumentException();
- }
- other.component = component;
- other.userId = userId;
+ return "EnforcedAdmin{" +
+ "component=" + component +
+ ", enforcedRestriction='" + enforcedRestriction +
+ ", userId=" + userId +
+ '}';
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/display/BrightnessUtils.java b/packages/SettingsLib/src/com/android/settingslib/display/BrightnessUtils.java
new file mode 100644
index 0000000..55723f9
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/display/BrightnessUtils.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.display;
+
+import android.util.MathUtils;
+
+public class BrightnessUtils {
+
+ public static final int GAMMA_SPACE_MAX = 1023;
+
+ // Hybrid Log Gamma constant values
+ private static final float R = 0.5f;
+ private static final float A = 0.17883277f;
+ private static final float B = 0.28466892f;
+ private static final float C = 0.55991073f;
+
+ /**
+ * A function for converting from the gamma space that the slider works in to the
+ * linear space that the setting works in.
+ *
+ * The gamma space effectively provides us a way to make linear changes to the slider that
+ * result in linear changes in perception. If we made changes to the slider in the linear space
+ * then we'd see an approximately logarithmic change in perception (c.f. Fechner's Law).
+ *
+ * Internally, this implements the Hybrid Log Gamma electro-optical transfer function, which is
+ * a slight improvement to the typical gamma transfer function for displays whose max
+ * brightness exceeds the 120 nit reference point, but doesn't set a specific reference
+ * brightness like the PQ function does.
+ *
+ * Note that this transfer function is only valid if the display's backlight value is a linear
+ * control. If it's calibrated to be something non-linear, then a different transfer function
+ * should be used.
+ *
+ * @param val The slider value.
+ * @param min The minimum acceptable value for the setting.
+ * @param max The maximum acceptable value for the setting.
+ * @return The corresponding setting value.
+ */
+ public static final int convertGammaToLinear(int val, int min, int max) {
+ final float normalizedVal = MathUtils.norm(0, GAMMA_SPACE_MAX, val);
+ final float ret;
+ if (normalizedVal <= R) {
+ ret = MathUtils.sq(normalizedVal / R);
+ } else {
+ ret = MathUtils.exp((normalizedVal - C) / A) + B;
+ }
+
+ // HLG is normalized to the range [0, 12], so we need to re-normalize to the range [0, 1]
+ // in order to derive the correct setting value.
+ return Math.round(MathUtils.lerp(min, max, ret / 12));
+ }
+
+ /**
+ * A function for converting from the linear space that the setting works in to the
+ * gamma space that the slider works in.
+ *
+ * The gamma space effectively provides us a way to make linear changes to the slider that
+ * result in linear changes in perception. If we made changes to the slider in the linear space
+ * then we'd see an approximately logarithmic change in perception (c.f. Fechner's Law).
+ *
+ * Internally, this implements the Hybrid Log Gamma opto-electronic transfer function, which is
+ * a slight improvement to the typical gamma transfer function for displays whose max
+ * brightness exceeds the 120 nit reference point, but doesn't set a specific reference
+ * brightness like the PQ function does.
+ *
+ * Note that this transfer function is only valid if the display's backlight value is a linear
+ * control. If it's calibrated to be something non-linear, then a different transfer function
+ * should be used.
+ *
+ * @param val The brightness setting value.
+ * @param min The minimum acceptable value for the setting.
+ * @param max The maximum acceptable value for the setting.
+ * @return The corresponding slider value
+ */
+ public static final int convertLinearToGamma(int val, int min, int max) {
+ // For some reason, HLG normalizes to the range [0, 12] rather than [0, 1]
+ final float normalizedVal = MathUtils.norm(min, max, val) * 12;
+ final float ret;
+ if (normalizedVal <= 1f) {
+ ret = MathUtils.sqrt(normalizedVal) * R;
+ } else {
+ ret = A * MathUtils.log(normalizedVal - B) + C;
+ }
+
+ return Math.round(MathUtils.lerp(0, GAMMA_SPACE_MAX, ret));
+ }
+}
diff --git a/packages/SettingsLib/src/com/android/settingslib/users/UserManagerHelper.java b/packages/SettingsLib/src/com/android/settingslib/users/UserManagerHelper.java
index 113256f..e2faf6a 100644
--- a/packages/SettingsLib/src/com/android/settingslib/users/UserManagerHelper.java
+++ b/packages/SettingsLib/src/com/android/settingslib/users/UserManagerHelper.java
@@ -354,20 +354,15 @@
return;
}
- if (userInfo.isGuest()) {
- switchToGuest(userInfo.name);
- return;
- }
-
switchToUserId(userInfo.id);
}
/**
- * Creates a guest session and switches into the guest session.
+ * Creates a new guest session and switches into the guest session.
*
* @param guestName Username for the guest user.
*/
- public void switchToGuest(String guestName) {
+ public void startNewGuestSession(String guestName) {
UserInfo guest = mUserManager.createGuest(mContext, guestName);
if (guest == null) {
// Couldn't create user, most likely because there are too many, but we haven't
@@ -375,6 +370,7 @@
Log.w(TAG, "can't create user.");
return;
}
+ assignDefaultIcon(guest);
switchToUserId(guest.id);
}
@@ -417,6 +413,27 @@
mUserManager.setUserName(user.id, name);
}
+ /**
+ * Gets a bitmap representing the user's default avatar.
+ *
+ * @param userInfo User whose avatar should be returned.
+ * @return Default user icon
+ */
+ public Bitmap getUserDefaultIcon(UserInfo userInfo) {
+ return UserIcons.convertToBitmap(
+ UserIcons.getDefaultUserIcon(mContext.getResources(), userInfo.id, false));
+ }
+
+ /**
+ * Gets a bitmap representing the default icon for a Guest user.
+ *
+ * @return Degault guest icon
+ */
+ public Bitmap getGuestDefaultIcon() {
+ return UserIcons.convertToBitmap(UserIcons.getDefaultUserIcon(
+ mContext.getResources(), UserHandle.USER_NULL, false));
+ }
+
private void registerReceiver() {
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_USER_REMOVED);
@@ -435,8 +452,7 @@
* @return Bitmap that has been assigned to the user.
*/
private Bitmap assignDefaultIcon(UserInfo userInfo) {
- Bitmap bitmap = UserIcons.convertToBitmap(
- UserIcons.getDefaultUserIcon(mContext.getResources(), userInfo.id, false));
+ Bitmap bitmap = userInfo.isGuest() ? getGuestDefaultIcon() : getUserDefaultIcon(userInfo);
mUserManager.setUserIcon(userInfo.id, bitmap);
return bitmap;
}
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/users/UserManagerHelperTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/users/UserManagerHelperTest.java
index 15f7770..54510b2 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/users/UserManagerHelperTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/users/UserManagerHelperTest.java
@@ -247,13 +247,13 @@
}
@Test
- public void switchToGuest() {
- mHelper.switchToGuest("Test Guest");
+ public void startNewGuestSession() {
+ mHelper.startNewGuestSession("Test Guest");
verify(mUserManager).createGuest(mContext, "Test Guest");
UserInfo guestInfo = new UserInfo(21, "Test Guest", UserInfo.FLAG_GUEST);
when(mUserManager.createGuest(mContext, "Test Guest")).thenReturn(guestInfo);
- mHelper.switchToGuest("Test Guest");
+ mHelper.startNewGuestSession("Test Guest");
verify(mActivityManager).switchUser(21);
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedLockUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedLockUtilsTest.java
index 5f60868..710dbc22 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedLockUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/RestrictedLockUtilsTest.java
@@ -32,6 +32,7 @@
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
+import android.os.UserHandle;
import android.os.UserManager;
import org.junit.Before;
@@ -42,6 +43,7 @@
import org.mockito.MockitoAnnotations;
import java.util.Arrays;
+import java.util.Collections;
@RunWith(SettingsLibRobolectricTestRunner.class)
public class RestrictedLockUtilsTest {
@@ -77,6 +79,42 @@
}
@Test
+ public void checkIfRestrictionEnforced_deviceOwner() {
+ UserManager.EnforcingUser enforcingUser = new UserManager.EnforcingUser(mUserId,
+ UserManager.RESTRICTION_SOURCE_DEVICE_OWNER);
+ final String userRestriction = UserManager.DISALLOW_UNINSTALL_APPS;
+ when(mUserManager.getUserRestrictionSources(userRestriction,
+ UserHandle.of(mUserId))).
+ thenReturn(Collections.singletonList(enforcingUser));
+ setUpDeviceOwner(mAdmin1);
+
+ EnforcedAdmin enforcedAdmin = RestrictedLockUtils.checkIfRestrictionEnforced(mContext,
+ userRestriction, mUserId);
+
+ assertThat(enforcedAdmin).isNotNull();
+ assertThat(enforcedAdmin.enforcedRestriction).isEqualTo(userRestriction);
+ assertThat(enforcedAdmin.component).isEqualTo(mAdmin1);
+ }
+
+ @Test
+ public void checkIfRestrictionEnforced_profileOwner() {
+ UserManager.EnforcingUser enforcingUser = new UserManager.EnforcingUser(mUserId,
+ UserManager.RESTRICTION_SOURCE_PROFILE_OWNER);
+ final String userRestriction = UserManager.DISALLOW_UNINSTALL_APPS;
+ when(mUserManager.getUserRestrictionSources(userRestriction,
+ UserHandle.of(mUserId))).
+ thenReturn(Collections.singletonList(enforcingUser));
+ setUpProfileOwner(mAdmin1, mUserId);
+
+ EnforcedAdmin enforcedAdmin = RestrictedLockUtils.checkIfRestrictionEnforced(mContext,
+ userRestriction, mUserId);
+
+ assertThat(enforcedAdmin).isNotNull();
+ assertThat(enforcedAdmin.enforcedRestriction).isEqualTo(userRestriction);
+ assertThat(enforcedAdmin.component).isEqualTo(mAdmin1);
+ }
+
+ @Test
public void checkIfDevicePolicyServiceDisabled_noEnforceAdminForManagedProfile() {
when(mContext.getSystemService(Context.DEVICE_POLICY_SERVICE)).thenReturn(null);
final EnforcedAdmin enforcedAdmin = RestrictedLockUtils.checkIfAccountManagementDisabled(
@@ -263,4 +301,12 @@
when(mDevicePolicyManager.getActiveAdminsAsUser(userId))
.thenReturn(Arrays.asList(activeAdmins));
}
+
+ private void setUpDeviceOwner(ComponentName admin) {
+ when(mDevicePolicyManager.getDeviceOwnerComponentOnAnyUser()).thenReturn(admin);
+ }
+
+ private void setUpProfileOwner(ComponentName admin, int userId) {
+ when(mDevicePolicyManager.getProfileOwnerAsUser(userId)).thenReturn(admin);
+ }
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/display/BrightnessUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/display/BrightnessUtilsTest.java
new file mode 100644
index 0000000..eb7ad6d
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/display/BrightnessUtilsTest.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settingslib.display;
+
+import static com.android.settingslib.display.BrightnessUtils.GAMMA_SPACE_MAX;
+import static com.google.common.truth.Truth.assertThat;
+
+import com.android.settingslib.SettingsLibRobolectricTestRunner;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(SettingsLibRobolectricTestRunner.class)
+public class BrightnessUtilsTest {
+
+ private static final int MIN = 1;
+ private static final int MAX = 255;
+
+ @Test
+ public void linearToGamma_minValue_shouldReturn0() {
+ assertThat(BrightnessUtils.convertLinearToGamma(MIN, MIN, MAX)).isEqualTo(0);
+ }
+
+ @Test
+ public void linearToGamma_maxValue_shouldReturnGammaSpaceMax() {
+ assertThat(BrightnessUtils.convertLinearToGamma(MAX, MIN, MAX)).isEqualTo(GAMMA_SPACE_MAX);
+ }
+
+ @Test
+ public void gammaToLinear_minValue_shouldReturnMin() {
+ assertThat(BrightnessUtils.convertGammaToLinear(MIN, MIN, MAX)).isEqualTo(MIN);
+ }
+
+ @Test
+ public void gammaToLinear_gammaSpaceValue_shouldReturnMax() {
+ assertThat(BrightnessUtils.convertGammaToLinear(GAMMA_SPACE_MAX, MIN, MAX)).isEqualTo(MAX);
+ }
+}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/users/UserManagerHelperRoboTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/users/UserManagerHelperRoboTest.java
index f2ea3a4..890abef 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/users/UserManagerHelperRoboTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/users/UserManagerHelperRoboTest.java
@@ -185,12 +185,7 @@
mHelper.switchToUser(createUserInfoForId(20));
assertThat(ShadowActivityManager.getShadow().getSwitchUserCalled()).isFalse();
- // Switching to Guest calls createGuest.
- UserInfo guestInfo = new UserInfo(21, "Test Guest", UserInfo.FLAG_GUEST);
- mHelper.switchToUser(guestInfo);
- verify(mUserManager).createGuest(mContext, "Test Guest");
-
- // Switching to non-current, non-guest user, simply calls switchUser.
+ // Switching to non-foreground user, simply calls switchUser.
UserInfo userToSwitchTo = new UserInfo(22, "Test User", 0);
mHelper.switchToUser(userToSwitchTo);
assertThat(ShadowActivityManager.getShadow().getSwitchUserCalled()).isTrue();
diff --git a/packages/SystemUI/res/layout/hybrid_notification.xml b/packages/SystemUI/res/layout/hybrid_notification.xml
index f4501da..bccf207 100644
--- a/packages/SystemUI/res/layout/hybrid_notification.xml
+++ b/packages/SystemUI/res/layout/hybrid_notification.xml
@@ -34,7 +34,6 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:singleLine="true"
- android:textAppearance="@*android:style/TextAppearance.Material.Notification"
style="?attr/hybridNotificationTextStyle"
/>
</com.android.systemui.statusbar.notification.HybridNotificationView>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/hybrid_overflow_number.xml b/packages/SystemUI/res/layout/hybrid_overflow_number.xml
index 792f424..08f8f94 100644
--- a/packages/SystemUI/res/layout/hybrid_overflow_number.xml
+++ b/packages/SystemUI/res/layout/hybrid_overflow_number.xml
@@ -16,10 +16,10 @@
-->
<TextView
xmlns:android="http://schemas.android.com/apk/res/android"
+ style="@*android:style/Widget.Material.Notification.Text"
android:id="@+id/notification_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:textAppearance="@*android:style/TextAppearance.Material.Notification"
android:paddingEnd="@dimen/group_overflow_number_padding"
android:gravity="end"
android:singleLine="true"
diff --git a/packages/SystemUI/res/layout/qs_tile_label.xml b/packages/SystemUI/res/layout/qs_tile_label.xml
index 49d142a..8ca867f 100644
--- a/packages/SystemUI/res/layout/qs_tile_label.xml
+++ b/packages/SystemUI/res/layout/qs_tile_label.xml
@@ -41,7 +41,6 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:clickable="false"
- android:maxLines="2"
android:padding="0dp"
android:gravity="center"
android:ellipsize="marquee"
diff --git a/packages/SystemUI/res/layout/quick_qs_status_icons.xml b/packages/SystemUI/res/layout/quick_qs_status_icons.xml
index 5f73bef..4301fdb 100644
--- a/packages/SystemUI/res/layout/quick_qs_status_icons.xml
+++ b/packages/SystemUI/res/layout/quick_qs_status_icons.xml
@@ -18,13 +18,15 @@
xmlns:systemui="http://schemas.android.com/apk/res-auto"
android:id="@+id/quick_qs_status_icons"
android:layout_width="match_parent"
- android:layout_height="20dp"
+ android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginBottom="14dp"
- android:layout_marginStart="8dp"
- android:layout_marginEnd="@dimen/notification_side_paddings"
+ android:layout_marginStart="@dimen/status_bar_padding_start"
+ android:layout_marginEnd="@dimen/status_bar_padding_end"
android:layout_below="@id/quick_status_bar_system_icons"
- android:paddingEnd="@dimen/status_bar_padding_end" >
+ android:clipChildren="false"
+ android:clipToPadding="false"
+ android:minHeight="20dp" >
<com.android.systemui.statusbar.policy.DateView
android:id="@+id/date"
diff --git a/packages/SystemUI/res/layout/quick_settings_header_info.xml b/packages/SystemUI/res/layout/quick_settings_header_info.xml
index 54baa4a..0892f73 100644
--- a/packages/SystemUI/res/layout/quick_settings_header_info.xml
+++ b/packages/SystemUI/res/layout/quick_settings_header_info.xml
@@ -45,14 +45,16 @@
android:layout_width="@dimen/qs_header_alarm_icon_size"
android:layout_height="@dimen/qs_header_alarm_icon_size"
android:src="@drawable/stat_sys_alarm"
- android:tint="?android:attr/textColorPrimary" />
+ android:tint="?android:attr/textColorPrimary"
+ android:visibility="gone"/>
<TextView
android:id="@+id/next_alarm_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/qs_header_alarm_text_margin_start"
- android:textAppearance="@style/TextAppearance.QS.TileLabel" />
+ android:textAppearance="@style/TextAppearance.QS.TileLabel"
+ android:visibility="gone"/>
<View
android:id="@+id/status_separator"
@@ -61,20 +63,23 @@
android:layout_marginStart="10dp"
android:layout_marginEnd="10dp"
android:background="@android:color/white"
- android:backgroundTint="?android:attr/textColorPrimary" />
+ android:backgroundTint="?android:attr/textColorPrimary"
+ android:visibility="gone"/>
<ImageView
android:id="@+id/ringer_mode_icon"
android:layout_width="@dimen/qs_header_alarm_icon_size"
android:layout_height="@dimen/qs_header_alarm_icon_size"
- android:tint="?android:attr/textColorPrimary" />
+ android:tint="?android:attr/textColorPrimary"
+ android:visibility="gone"/>
<TextView
android:id="@+id/ringer_mode_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/qs_header_alarm_text_margin_start"
- android:textAppearance="@style/TextAppearance.QS.TileLabel" />
+ android:textAppearance="@style/TextAppearance.QS.TileLabel"
+ android:visibility="gone"/>
</LinearLayout>
diff --git a/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml b/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml
index a9fe862..d40534e 100644
--- a/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml
+++ b/packages/SystemUI/res/layout/quick_status_bar_header_system_icons.xml
@@ -20,15 +20,12 @@
android:id="@+id/quick_status_bar_system_icons"
android:layout_width="match_parent"
android:layout_height="@*android:dimen/quick_qs_offset_height"
- android:layout_marginRight="@dimen/notification_side_paddings"
- android:layout_marginLeft="@dimen/notification_side_paddings"
- android:layout_alignParentEnd="true"
android:clipChildren="false"
android:clipToPadding="false"
android:gravity="center"
+ android:orientation="horizontal"
android:paddingStart="@dimen/status_bar_padding_start"
- android:paddingEnd="@dimen/status_bar_padding_end"
- android:orientation="horizontal">
+ android:paddingEnd="@dimen/status_bar_padding_end" >
<com.android.systemui.statusbar.policy.Clock
android:id="@+id/clock"
diff --git a/packages/SystemUI/res/layout/volume_dnd_icon.xml b/packages/SystemUI/res/layout/volume_dnd_icon.xml
index acf9aed..ac235b7 100644
--- a/packages/SystemUI/res/layout/volume_dnd_icon.xml
+++ b/packages/SystemUI/res/layout/volume_dnd_icon.xml
@@ -15,11 +15,11 @@
-->
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="@dimen/volume_dialog_panel_width"
+ android:id="@+id/dnd_icon"
+ android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
- android:id="@+id/dnd_icon"
android:layout_width="14dp"
android:layout_height="14dp"
android:layout_marginTop="6dp"
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 875000c..ac9fb2b 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -326,7 +326,7 @@
<dimen name="pull_span_min">25dp</dimen>
<dimen name="qs_tile_height">106dp</dimen>
- <dimen name="qs_tile_layout_margin_side">9dp</dimen>
+ <dimen name="qs_tile_layout_margin_side">6dp</dimen>
<dimen name="qs_tile_margin_horizontal">18dp</dimen>
<dimen name="qs_tile_margin_vertical">24dp</dimen>
<dimen name="qs_tile_margin_top_bottom">12dp</dimen>
@@ -917,6 +917,8 @@
<dimen name="edge_margin">8dp</dimen>
<dimen name="rounded_corner_radius">0dp</dimen>
+ <dimen name="rounded_corner_radius_top">0dp</dimen>
+ <dimen name="rounded_corner_radius_bottom">0dp</dimen>
<dimen name="rounded_corner_content_padding">0dp</dimen>
<dimen name="nav_content_padding">0dp</dimen>
<dimen name="nav_quick_scrub_track_edge_padding">42dp</dimen>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index d82b00c..697ab06 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2223,4 +2223,6 @@
<!-- An action on the dialog that tells that scheduled (i.e. automatic) battery saver: user acknowledges and closes the dialog. [CHAR LIMIT=NONE]-->
<string name="auto_saver_okay_action">Got it</string>
+ <!-- URl of the webpage that explains battery saver. -->
+ <string name="help_uri_battery_saver_learn_more_link_target" translatable="false"></string>
</resources>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 804ca2c..1e19534 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -137,7 +137,8 @@
<item name="android:layout_marginTop">4dp</item>
</style>
- <style name="hybrid_notification_text">
+ <style name="hybrid_notification_text"
+ parent="@*android:style/Widget.Material.Notification.Text">
<item name="android:paddingEnd">4dp</item>
</style>
@@ -360,10 +361,6 @@
parent="@*android:style/TextAppearance.Material.Notification.Info">
</style>
- <style name="TextAppearance.Material.Notification.HybridNotificationDivider"
- parent="@*android:style/TextAppearance.Material.Notification">
- </style>
-
<style name="SearchPanelCircle">
<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">match_parent</item>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/NavigationBarCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/NavigationBarCompat.java
index 8d451c1..5a0dddc 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/NavigationBarCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/NavigationBarCompat.java
@@ -33,10 +33,21 @@
* touch slop is when the respected operation will occur when exceeded. Touch slop must be
* larger than the drag slop.
*/
- public static final int QUICK_STEP_DRAG_SLOP_PX = convertDpToPixel(10);
- public static final int QUICK_SCRUB_DRAG_SLOP_PX = convertDpToPixel(20);
- public static final int QUICK_STEP_TOUCH_SLOP_PX = convertDpToPixel(24);
- public static final int QUICK_SCRUB_TOUCH_SLOP_PX = convertDpToPixel(35);
+ public static int getQuickStepDragSlopPx() {
+ return convertDpToPixel(10);
+ }
+
+ public static int getQuickScrubDragSlopPx() {
+ return convertDpToPixel(20);
+ }
+
+ public static int getQuickStepTouchSlopPx() {
+ return convertDpToPixel(24);
+ }
+
+ public static int getQuickScrubTouchSlopPx() {
+ return convertDpToPixel(35);
+ }
@Retention(RetentionPolicy.SOURCE)
@IntDef({HIT_TARGET_NONE, HIT_TARGET_BACK, HIT_TARGET_HOME, HIT_TARGET_OVERVIEW})
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index c826aaa..d24675c 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -68,6 +68,7 @@
import android.telephony.SubscriptionManager.OnSubscriptionsChangedListener;
import android.telephony.TelephonyManager;
import android.util.Log;
+import android.util.Slog;
import android.util.SparseBooleanArray;
import android.util.SparseIntArray;
@@ -87,7 +88,6 @@
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.HashMap;
-import java.util.HashSet;
import java.util.List;
import java.util.Map.Entry;
@@ -400,9 +400,16 @@
// Hack level over 9000: Because the subscription id is not yet valid when we see the
// first update in handleSimStateChange, we need to force refresh all all SIM states
// so the subscription id for them is consistent.
- List<Integer> changedSubscriptionIds = refreshSimState(subscriptionInfos);
- for (int i = 0; i < changedSubscriptionIds.size(); i++) {
- SimData data = mSimDatas.get(changedSubscriptionIds.get(i));
+ ArrayList<SubscriptionInfo> changedSubscriptions = new ArrayList<>();
+ for (int i = 0; i < subscriptionInfos.size(); i++) {
+ SubscriptionInfo info = subscriptionInfos.get(i);
+ boolean changed = refreshSimState(info.getSubscriptionId(), info.getSimSlotIndex());
+ if (changed) {
+ changedSubscriptions.add(info);
+ }
+ }
+ for (int i = 0; i < changedSubscriptions.size(); i++) {
+ SimData data = mSimDatas.get(changedSubscriptions.get(i).getSubscriptionId());
for (int j = 0; j < mCallbacks.size(); j++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(j).get();
if (cb != null) {
@@ -1839,61 +1846,34 @@
};
/**
- * @return A list of changed subscriptions, maybe empty but never null
+ * @return true if and only if the state has changed for the specified {@code slotId}
*/
- private List<Integer> refreshSimState(final List<SubscriptionInfo> activeSubscriptionInfos) {
+ private boolean refreshSimState(int subId, int slotId) {
// This is awful. It exists because there are two APIs for getting the SIM status
// that don't return the complete set of values and have different types. In Keyguard we
// need IccCardConstants, but TelephonyManager would only give us
// TelephonyManager.SIM_STATE*, so we retrieve it manually.
final TelephonyManager tele = TelephonyManager.from(mContext);
- ArrayList<Integer> changedSubscriptionIds = new ArrayList<>();
- HashSet<Integer> activeSubIds = new HashSet<>();
- HashSet<Integer> activeSlotIds = new HashSet<>();
-
- for (SubscriptionInfo info : activeSubscriptionInfos) {
- int subId = info.getSubscriptionId();
- int slotId = info.getSimSlotIndex();
- int simState = tele.getSimState(slotId);
- State state;
- try {
- state = State.intToState(simState);
- } catch(IllegalArgumentException ex) {
- Log.w(TAG, "Unknown sim state: " + simState);
- state = State.UNKNOWN;
- }
-
- SimData data = mSimDatas.get(subId);
- final boolean changed;
- if (data == null) {
- data = new SimData(state, slotId, subId);
- mSimDatas.put(subId, data);
- changed = true; // no data yet; force update
- } else {
- changed = data.simState != state;
- data.simState = state;
- }
- if (changed) {
- changedSubscriptionIds.add(subId);
- }
-
- activeSubIds.add(subId);
- activeSlotIds.add(slotId);
+ int simState = tele.getSimState(slotId);
+ State state;
+ try {
+ state = State.intToState(simState);
+ } catch(IllegalArgumentException ex) {
+ Log.w(TAG, "Unknown sim state: " + simState);
+ state = State.UNKNOWN;
}
-
- for (SimData data : mSimDatas.values()) {
- if (!activeSubIds.contains(data.subId)
- && !activeSlotIds.contains(data.slotId)
- && data.simState != State.ABSENT) {
- // for the inactive subscriptions, reset state to ABSENT
- if (DEBUG_SIM_STATES) Log.d(TAG, "reset state to ABSENT for subId:" + data.subId);
- data.simState = State.ABSENT;
- changedSubscriptionIds.add(data.subId);
- }
+ SimData data = mSimDatas.get(subId);
+ final boolean changed;
+ if (data == null) {
+ data = new SimData(state, slotId, subId);
+ mSimDatas.put(subId, data);
+ changed = true; // no data yet; force update
+ } else {
+ changed = data.simState != state;
+ data.simState = state;
}
-
- return changedSubscriptionIds;
+ return changed;
}
public static boolean isSimPinSecure(IccCardConstants.State state) {
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
index 72f6cdc..8d32e4d 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
@@ -78,6 +78,8 @@
SystemProperties.getBoolean("debug.screenshot_rounded_corners", false);
private int mRoundedDefault;
+ private int mRoundedDefaultTop;
+ private int mRoundedDefaultBottom;
private View mOverlay;
private View mBottomOverlay;
private float mDensity;
@@ -89,9 +91,14 @@
mWindowManager = mContext.getSystemService(WindowManager.class);
mRoundedDefault = mContext.getResources().getDimensionPixelSize(
R.dimen.rounded_corner_radius);
- if (mRoundedDefault != 0 || shouldDrawCutout()) {
+ mRoundedDefaultTop = mContext.getResources().getDimensionPixelSize(
+ R.dimen.rounded_corner_radius_top);
+ mRoundedDefaultBottom = mContext.getResources().getDimensionPixelSize(
+ R.dimen.rounded_corner_radius_bottom);
+ if (hasRoundedCorners() || shouldDrawCutout()) {
setupDecorations();
}
+
int padding = mContext.getResources().getDimensionPixelSize(
R.dimen.rounded_corner_content_padding);
if (padding != 0) {
@@ -208,11 +215,15 @@
private void updateWindowVisibility(View overlay) {
boolean visibleForCutout = shouldDrawCutout()
&& overlay.findViewById(R.id.display_cutout).getVisibility() == View.VISIBLE;
- boolean visibleForRoundedCorners = mRoundedDefault > 0;
+ boolean visibleForRoundedCorners = hasRoundedCorners();
overlay.setVisibility(visibleForCutout || visibleForRoundedCorners
? View.VISIBLE : View.GONE);
}
+ private boolean hasRoundedCorners() {
+ return mRoundedDefault > 0 || mRoundedDefaultBottom > 0 || mRoundedDefaultTop > 0;
+ }
+
private boolean shouldDrawCutout() {
return shouldDrawCutout(mContext);
}
@@ -284,14 +295,26 @@
if (mOverlay == null) return;
if (SIZE.equals(key)) {
int size = mRoundedDefault;
- try {
- size = (int) (Integer.parseInt(newValue) * mDensity);
- } catch (Exception e) {
+ int sizeTop = mRoundedDefaultTop;
+ int sizeBottom = mRoundedDefaultBottom;
+ if (newValue != null) {
+ try {
+ size = (int) (Integer.parseInt(newValue) * mDensity);
+ } catch (Exception e) {
+ }
}
- setSize(mOverlay.findViewById(R.id.left), size);
- setSize(mOverlay.findViewById(R.id.right), size);
- setSize(mBottomOverlay.findViewById(R.id.left), size);
- setSize(mBottomOverlay.findViewById(R.id.right), size);
+
+ if (sizeTop == 0) {
+ sizeTop = size;
+ }
+ if (sizeBottom == 0) {
+ sizeBottom = size;
+ }
+
+ setSize(mOverlay.findViewById(R.id.left), sizeTop);
+ setSize(mOverlay.findViewById(R.id.right), sizeTop);
+ setSize(mBottomOverlay.findViewById(R.id.left), sizeBottom);
+ setSize(mBottomOverlay.findViewById(R.id.right), sizeBottom);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 0be3d70..ca92d35 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -1854,6 +1854,8 @@
synchronized (KeyguardViewMediator.this) {
if (!mHiding) {
+ // Tell ActivityManager that we canceled the keyguardExitAnimation.
+ setShowingLocked(mShowing, mAodShowing, mSecondaryDisplayShowing, true /* force */);
return;
}
mHiding = false;
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
index d860fc5..c6bb17c 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
@@ -19,17 +19,29 @@
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
+import android.content.ActivityNotFoundException;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.media.AudioAttributes;
+import android.net.Uri;
import android.os.Handler;
import android.os.Looper;
import android.os.PowerManager;
import android.os.UserHandle;
import android.support.annotation.VisibleForTesting;
+import android.text.Annotation;
+import android.text.Layout;
+import android.text.SpannableString;
+import android.text.SpannableStringBuilder;
+import android.text.TextPaint;
+import android.text.TextUtils;
+import android.text.method.LinkMovementMethod;
+import android.text.style.URLSpan;
+import android.util.Log;
import android.util.Slog;
+import android.view.View;
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
import com.android.settingslib.Utils;
@@ -42,6 +54,8 @@
import java.io.PrintWriter;
import java.text.NumberFormat;
+import java.util.Locale;
+import java.util.Objects;
public class PowerNotificationWarnings implements PowerUI.WarningsUI {
private static final String TAG = PowerUI.TAG + ".Notification";
@@ -87,6 +101,8 @@
private static final String SETTINGS_ACTION_OPEN_BATTERY_SAVER_SETTING =
"android.settings.BATTERY_SAVER_SETTINGS";
+ private static final String BATTERY_SAVER_DESCRIPTION_URL_KEY = "url";
+
private static final AudioAttributes AUDIO_ATTRIBUTES = new AudioAttributes.Builder()
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
.setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
@@ -461,7 +477,16 @@
if (mSaverConfirmation != null) return;
final SystemUIDialog d = new SystemUIDialog(mContext);
d.setTitle(R.string.battery_saver_confirmation_title);
- d.setMessage(com.android.internal.R.string.battery_saver_description);
+ d.setMessage(getBatterySaverDescription());
+
+ // Sad hack for http://b/78261259 and http://b/78298335. Otherwise "Battery" may be split
+ // into "Bat-tery".
+ if (isEnglishLocale()) {
+ d.setMessageHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE);
+ }
+ // We need to set LinkMovementMethod to make the link clickable.
+ d.setMessageMovementMethod(LinkMovementMethod.getInstance());
+
d.setNegativeButton(android.R.string.cancel, null);
d.setPositiveButton(R.string.battery_saver_confirmation_ok,
(dialog, which) -> setSaverMode(true, false));
@@ -471,6 +496,79 @@
mSaverConfirmation = d;
}
+ private boolean isEnglishLocale() {
+ return Objects.equals(Locale.getDefault().getLanguage(),
+ Locale.ENGLISH.getLanguage());
+ }
+
+ /**
+ * Generates the message for the "want to start battery saver?" dialog with a "learn more" link.
+ */
+ private CharSequence getBatterySaverDescription() {
+ final String learnMoreUrl = mContext.getText(
+ R.string.help_uri_battery_saver_learn_more_link_target).toString();
+
+ // If there's no link, use the string with no "learn more".
+ if (TextUtils.isEmpty(learnMoreUrl)) {
+ return mContext.getText(
+ com.android.internal.R.string.battery_saver_description);
+ }
+
+ // If we have a link, use the string with the "learn more" link.
+ final CharSequence rawText = mContext.getText(
+ com.android.internal.R.string.battery_saver_description_with_learn_more);
+ final SpannableString message = new SpannableString(rawText);
+ final SpannableStringBuilder builder = new SpannableStringBuilder(message);
+
+ // Look for the "learn more" part of the string, and set a URL span on it.
+ // We use a customized URLSpan to add FLAG_RECEIVER_FOREGROUND to the intent, and
+ // also to close the dialog.
+ for (Annotation annotation : message.getSpans(0, message.length(), Annotation.class)) {
+ final String key = annotation.getValue();
+
+ if (!BATTERY_SAVER_DESCRIPTION_URL_KEY.equals(key)) {
+ continue;
+ }
+ final int start = message.getSpanStart(annotation);
+ final int end = message.getSpanEnd(annotation);
+
+ // Replace the "learn more" with a custom URL span, with
+ // - No underline.
+ // - When clicked, close the dialog and the notification shade.
+ final URLSpan urlSpan = new URLSpan(learnMoreUrl) {
+ @Override
+ public void updateDrawState(TextPaint ds) {
+ super.updateDrawState(ds);
+ ds.setUnderlineText(false);
+ }
+
+ @Override
+ public void onClick(View widget) {
+ // Close the parent dialog.
+ if (mSaverConfirmation != null) {
+ mSaverConfirmation.dismiss();
+ }
+ // Also close the notification shade, if it's open.
+ mContext.sendBroadcast(
+ new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS)
+ .setFlags(Intent.FLAG_RECEIVER_FOREGROUND));
+
+ final Uri uri = Uri.parse(getURL());
+ Context context = widget.getContext();
+ Intent intent = new Intent(Intent.ACTION_VIEW, uri)
+ .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ try {
+ context.startActivity(intent);
+ } catch (ActivityNotFoundException e) {
+ Log.w(TAG, "Activity was not found for intent, " + intent.toString());
+ }
+ }
+ };
+ builder.setSpan(urlSpan, start, end, message.getSpanFlags(urlSpan));
+ }
+ return builder;
+ }
+
private void showAutoSaverEnabledConfirmation() {
if (mSaverEnabledConfirmation != null) return;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
index a44f9433..d8bf990 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
@@ -40,7 +40,7 @@
private int mHeightOverride = -1;
private QSPanel mQSPanel;
private View mQSDetail;
- private View mHeader;
+ private QuickStatusBarHeader mHeader;
private float mQsExpansion;
private QSCustomizer mQSCustomizer;
private View mQSFooter;
@@ -178,7 +178,7 @@
setMargins(mBackground);
setMargins(mQSFooter);
mQSPanel.setMargins(mSideMargins);
- setMargins(mHeader);
+ mHeader.setMargins(mSideMargins);
}
private void setMargins(View view) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
index f027c4b..ad2efbd 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickStatusBarHeader.java
@@ -41,6 +41,7 @@
import android.view.View;
import android.view.WindowInsets;
import android.widget.ImageView;
+import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;
@@ -591,4 +592,16 @@
public static float getColorIntensity(@ColorInt int color) {
return color == Color.WHITE ? 0 : 1;
}
+
+ public void setMargins(int sideMargins) {
+ for (int i = 0; i < getChildCount(); i++) {
+ View v = getChildAt(i);
+ if (v == mSystemIconsView || v == mQuickQsStatusIcons || v == mHeaderQsPanel) {
+ continue;
+ }
+ RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams) v.getLayoutParams();
+ lp.leftMargin = sideMargins;
+ lp.rightMargin = sideMargins;
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java
index 5649f7f..22ad550 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileView.java
@@ -36,6 +36,7 @@
/** View that represents a standard quick settings tile. **/
public class QSTileView extends QSTileBaseView {
+ private static final int MAX_LABEL_LINES = 2;
private static final boolean DUAL_TARGET_ALLOWED = false;
private View mDivider;
protected TextView mLabel;
@@ -98,9 +99,10 @@
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- // Remeasure view if the secondary label text will be cut off.
- if (!TextUtils.isEmpty(mSecondLine.getText())
- && mSecondLine.getLineHeight() > mSecondLine.getHeight()) {
+ // Remeasure view if the primary label requires more then 2 lines or the secondary label
+ // text will be cut off.
+ if (mLabel.getLineCount() > MAX_LABEL_LINES || !TextUtils.isEmpty(mSecondLine.getText())
+ && mSecondLine.getLineHeight() > mSecondLine.getHeight()) {
mLabel.setSingleLine();
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
index a25c466..cd00311 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
@@ -124,8 +124,9 @@
final boolean transientEnabling = arg == ARG_SHOW_TRANSIENT_ENABLING;
final boolean enabled = transientEnabling || mController.isBluetoothEnabled();
final boolean connected = mController.isBluetoothConnected();
- state.isTransient = transientEnabling || mController.isBluetoothConnecting()
- || mController.getBluetoothState() == BluetoothAdapter.STATE_TURNING_ON;
+ final boolean connecting = mController.isBluetoothConnecting();
+ state.isTransient = transientEnabling || connecting ||
+ mController.getBluetoothState() == BluetoothAdapter.STATE_TURNING_ON;
state.dualTarget = true;
state.value = enabled;
if (state.slash == null) {
@@ -134,7 +135,7 @@
state.slash.isSlashed = !enabled;
state.label = mContext.getString(R.string.quick_settings_bluetooth_label);
state.secondaryLabel = TextUtils.emptyIfNull(
- getSecondaryLabel(enabled, connected, state.isTransient));
+ getSecondaryLabel(enabled, connecting, connected, state.isTransient));
if (enabled) {
if (connected) {
state.icon = new BluetoothConnectedTileIcon();
@@ -170,13 +171,17 @@
* Returns the secondary label to use for the given bluetooth connection in the form of the
* battery level or bluetooth profile name. If the bluetooth is disabled, there's no connected
* devices, or we can't map the bluetooth class to a profile, this instead returns {@code null}.
- *
* @param enabled whether bluetooth is enabled
+ * @param connecting whether bluetooth is connecting to a device
* @param connected whether there's a device connected via bluetooth
* @param isTransient whether bluetooth is currently in a transient state turning on
*/
@Nullable
- private String getSecondaryLabel(boolean enabled, boolean connected, boolean isTransient) {
+ private String getSecondaryLabel(boolean enabled, boolean connecting, boolean connected,
+ boolean isTransient) {
+ if (connecting) {
+ return mContext.getString(R.string.quick_settings_connecting);
+ }
if (isTransient) {
return mContext.getString(R.string.quick_settings_bluetooth_secondary_label_transient);
}
diff --git a/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java b/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java
index 1736f38..be0aa11 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/BrightnessController.java
@@ -16,6 +16,10 @@
package com.android.systemui.settings;
+import static com.android.settingslib.display.BrightnessUtils.GAMMA_SPACE_MAX;
+import static com.android.settingslib.display.BrightnessUtils.convertGammaToLinear;
+import static com.android.settingslib.display.BrightnessUtils.convertLinearToGamma;
+
import android.animation.ValueAnimator;
import android.content.ContentResolver;
import android.content.Context;
@@ -24,7 +28,6 @@
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Handler;
-import android.os.IPowerManager;
import android.os.Looper;
import android.os.Message;
import android.os.PowerManager;
@@ -36,7 +39,6 @@
import android.service.vr.IVrManager;
import android.service.vr.IVrStateCallbacks;
import android.util.Log;
-import android.util.MathUtils;
import android.widget.ImageView;
import com.android.internal.logging.MetricsLogger;
@@ -50,15 +52,8 @@
private static final String TAG = "StatusBar.BrightnessController";
private static final boolean SHOW_AUTOMATIC_ICON = false;
- private static final int SLIDER_MAX = 1023;
private static final int SLIDER_ANIMATION_DURATION = 3000;
- // Hybrid Log Gamma constant values
- private static final float R = 0.5f;
- private static final float A = 0.17883277f;
- private static final float B = 0.28466892f;
- private static final float C = 0.55991073f;
-
private static final int MSG_UPDATE_ICON = 0;
private static final int MSG_UPDATE_SLIDER = 1;
private static final int MSG_SET_CHECKED = 2;
@@ -273,7 +268,7 @@
mContext = context;
mIcon = icon;
mControl = control;
- mControl.setMax(SLIDER_MAX);
+ mControl.setMax(GAMMA_SPACE_MAX);
mBackgroundHandler = new Handler((Looper) Dependency.get(Dependency.BG_LOOPER));
mUserTracker = new CurrentUserTracker(mContext) {
@Override
@@ -481,76 +476,4 @@
mSliderAnimator.start();
}
- /**
- * A function for converting from the linear space that the setting works in to the
- * gamma space that the slider works in.
- *
- * The gamma space effectively provides us a way to make linear changes to the slider that
- * result in linear changes in perception. If we made changes to the slider in the linear space
- * then we'd see an approximately logarithmic change in perception (c.f. Fechner's Law).
- *
- * Internally, this implements the Hybrid Log Gamma opto-electronic transfer function, which is
- * a slight improvement to the typical gamma transfer function for displays whose max
- * brightness exceeds the 120 nit reference point, but doesn't set a specific reference
- * brightness like the PQ function does.
- *
- * Note that this transfer function is only valid if the display's backlight value is a linear
- * control. If it's calibrated to be something non-linear, then a different transfer function
- * should be used.
- *
- * @param val The brightness setting value.
- * @param min The minimum acceptable value for the setting.
- * @param max The maximum acceptable value for the setting.
- *
- * @return The corresponding slider value
- */
- private static final int convertLinearToGamma(int val, int min, int max) {
- // For some reason, HLG normalizes to the range [0, 12] rather than [0, 1]
- final float normalizedVal = MathUtils.norm(min, max, val) * 12;
- final float ret;
- if (normalizedVal <= 1f) {
- ret = MathUtils.sqrt(normalizedVal) * R;
- } else {
- ret = A * MathUtils.log(normalizedVal - B) + C;
- }
-
- return Math.round(MathUtils.lerp(0, SLIDER_MAX, ret));
- }
-
- /**
- * A function for converting from the gamma space that the slider works in to the
- * linear space that the setting works in.
- *
- * The gamma space effectively provides us a way to make linear changes to the slider that
- * result in linear changes in perception. If we made changes to the slider in the linear space
- * then we'd see an approximately logarithmic change in perception (c.f. Fechner's Law).
- *
- * Internally, this implements the Hybrid Log Gamma electro-optical transfer function, which is
- * a slight improvement to the typical gamma transfer function for displays whose max
- * brightness exceeds the 120 nit reference point, but doesn't set a specific reference
- * brightness like the PQ function does.
- *
- * Note that this transfer function is only valid if the display's backlight value is a linear
- * control. If it's calibrated to be something non-linear, then a different transfer function
- * should be used.
- *
- * @param val The slider value.
- * @param min The minimum acceptable value for the setting.
- * @param max The maximum acceptable value for the setting.
- *
- * @return The corresponding setting value.
- */
- private static final int convertGammaToLinear(int val, int min, int max) {
- final float normalizedVal = MathUtils.norm(0, SLIDER_MAX, val);
- final float ret;
- if (normalizedVal <= R) {
- ret = MathUtils.sq(normalizedVal/R);
- } else {
- ret = MathUtils.exp((normalizedVal - C) / A) + B;
- }
-
- // HLG is normalized to the range [0, 12], so we need to re-normalize to the range [0, 1]
- // in order to derive the correct setting value.
- return Math.round(MathUtils.lerp(min, max, ret / 12));
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index 44136c5..d3caf03 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -407,13 +407,14 @@
boolean needsContinuousClipping = ViewState.isAnimatingY(icon) && !mAmbientState.isDark();
boolean isContinuousClipping = icon.getTag(TAG_CONTINUOUS_CLIPPING) != null;
if (needsContinuousClipping && !isContinuousClipping) {
+ final ViewTreeObserver observer = icon.getViewTreeObserver();
ViewTreeObserver.OnPreDrawListener predrawListener =
new ViewTreeObserver.OnPreDrawListener() {
@Override
public boolean onPreDraw() {
boolean animatingY = ViewState.isAnimatingY(icon);
- if (!animatingY || !icon.isAttachedToWindow()) {
- icon.getViewTreeObserver().removeOnPreDrawListener(this);
+ if (!animatingY) {
+ observer.removeOnPreDrawListener(this);
icon.setTag(TAG_CONTINUOUS_CLIPPING, null);
return true;
}
@@ -421,7 +422,20 @@
return true;
}
};
- icon.getViewTreeObserver().addOnPreDrawListener(predrawListener);
+ observer.addOnPreDrawListener(predrawListener);
+ icon.addOnAttachStateChangeListener(new OnAttachStateChangeListener() {
+ @Override
+ public void onViewAttachedToWindow(View v) {
+ }
+
+ @Override
+ public void onViewDetachedFromWindow(View v) {
+ if (v == icon) {
+ observer.removeOnPreDrawListener(predrawListener);
+ icon.setTag(TAG_CONTINUOUS_CLIPPING, null);
+ }
+ }
+ });
icon.setTag(TAG_CONTINUOUS_CLIPPING, predrawListener);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java
index f4e45812..5748ec9b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarMobileView.java
@@ -100,13 +100,13 @@
}
if (mState == null) {
- mState = state;
+ mState = state.copy();
initViewState();
return;
}
if (!mState.equals(state)) {
- updateState(state);
+ updateState(state.copy());
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarWifiView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarWifiView.java
index 0e2714d..bf94c1f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarWifiView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarWifiView.java
@@ -64,6 +64,7 @@
private WifiIconState mState;
private String mSlot;
private float mDarkIntensity = 0;
+ private int mVisibleState = -1;
private ContextThemeWrapper mDarkContext;
private ContextThemeWrapper mLightContext;
@@ -73,6 +74,7 @@
StatusBarWifiView v = (StatusBarWifiView) inflater.inflate(R.layout.status_bar_wifi_group, null);
v.setSlot(slot);
v.init();
+ v.setVisibleState(STATE_ICON);
return v;
}
@@ -123,6 +125,11 @@
@Override
public void setVisibleState(int state) {
+ if (state == mVisibleState) {
+ return;
+ }
+ mVisibleState = state;
+
switch (state) {
case STATE_ICON:
mWifiGroup.setVisibility(View.VISIBLE);
@@ -139,12 +146,6 @@
}
}
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- int width = MeasureSpec.getSize(widthMeasureSpec);
- }
-
private void init() {
int dualToneLightTheme = Utils.getThemeAttr(mContext, R.attr.lightIconTheme);
int dualToneDarkTheme = Utils.getThemeAttr(mContext, R.attr.darkIconTheme);
@@ -180,12 +181,12 @@
}
if (mState == null) {
- mState = state;
+ mState = state.copy();
initViewState();
}
if (!mState.equals(state)) {
- updateState(state);
+ updateState(state.copy());
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java
index 1148fad..5768fa2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/UserGridRecyclerView.java
@@ -207,7 +207,7 @@
// If the user selects Guest, start the guest session.
if (userRecord.mIsStartGuestSession) {
- mUserManagerHelper.switchToGuest(mGuestName);
+ mUserManagerHelper.startNewGuestSession(mGuestName);
return;
}
@@ -241,8 +241,7 @@
private Bitmap getUserRecordIcon(UserRecord userRecord) {
if (userRecord.mIsStartGuestSession) {
- return UserIcons.convertToBitmap(UserIcons.getDefaultUserIcon(
- mContext.getResources(), UserHandle.USER_NULL, false));
+ return mUserManagerHelper.getGuestDefaultIcon();
}
if (userRecord.mIsAddUser) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
index b8bce95..20ab64c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/ActivityLaunchAnimator.java
@@ -123,83 +123,102 @@
IRemoteAnimationFinishedCallback iRemoteAnimationFinishedCallback)
throws RemoteException {
mSourceNotification.post(() -> {
- for (RemoteAnimationTarget app : remoteAnimationTargets) {
- if (app.mode == RemoteAnimationTarget.MODE_OPENING) {
- setExpandAnimationRunning(true);
- mInstantCollapsePanel = app.position.y == 0
- && app.sourceContainerBounds.height()
- >= mNotificationPanel.getHeight();
- if (!mInstantCollapsePanel) {
- mNotificationPanel.collapseWithDuration(ANIMATION_DURATION);
+ RemoteAnimationTarget primary = getPrimaryRemoteAnimationTarget(
+ remoteAnimationTargets);
+ if (primary == null) {
+ setAnimationPending(false);
+ invokeCallback(iRemoteAnimationFinishedCallback);
+ return;
+ }
+
+ setExpandAnimationRunning(true);
+ mInstantCollapsePanel = primary.position.y == 0
+ && primary.sourceContainerBounds.height()
+ >= mNotificationPanel.getHeight();
+ if (!mInstantCollapsePanel) {
+ mNotificationPanel.collapseWithDuration(ANIMATION_DURATION);
+ }
+ ValueAnimator anim = ValueAnimator.ofFloat(0, 1);
+ mParams.startPosition = mSourceNotification.getLocationOnScreen();
+ mParams.startTranslationZ = mSourceNotification.getTranslationZ();
+ mParams.startClipTopAmount = mSourceNotification.getClipTopAmount();
+ if (mSourceNotification.isChildInGroup()) {
+ int parentClip = mSourceNotification
+ .getNotificationParent().getClipTopAmount();
+ mParams.parentStartClipTopAmount = parentClip;
+ // We need to calculate how much the child is clipped by the parent
+ // because children always have 0 clipTopAmount
+ if (parentClip != 0) {
+ float childClip = parentClip
+ - mSourceNotification.getTranslationY();
+ if (childClip > 0.0f) {
+ mParams.startClipTopAmount = (int) Math.ceil(childClip);
}
- ValueAnimator anim = ValueAnimator.ofFloat(0, 1);
- mParams.startPosition = mSourceNotification.getLocationOnScreen();
- mParams.startTranslationZ = mSourceNotification.getTranslationZ();
- mParams.startClipTopAmount = mSourceNotification.getClipTopAmount();
- if (mSourceNotification.isChildInGroup()) {
- int parentClip = mSourceNotification
- .getNotificationParent().getClipTopAmount();
- mParams.parentStartClipTopAmount = parentClip;
- // We need to calculate how much the child is clipped by the parent
- // because children always have 0 clipTopAmount
- if (parentClip != 0) {
- float childClip = parentClip
- - mSourceNotification.getTranslationY();
- if (childClip > 0.0f) {
- mParams.startClipTopAmount = (int) Math.ceil(childClip);
- }
- }
- }
- int targetWidth = app.sourceContainerBounds.width();
- int notificationHeight = mSourceNotification.getActualHeight()
- - mSourceNotification.getClipBottomAmount();
- int notificationWidth = mSourceNotification.getWidth();
- anim.setDuration(ANIMATION_DURATION);
- anim.setInterpolator(Interpolators.LINEAR);
- anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- mParams.linearProgress = animation.getAnimatedFraction();
- float progress
- = Interpolators.FAST_OUT_SLOW_IN.getInterpolation(
- mParams.linearProgress);
- int newWidth = (int) MathUtils.lerp(notificationWidth,
- targetWidth, progress);
- mParams.left = (int) ((targetWidth - newWidth) / 2.0f);
- mParams.right = mParams.left + newWidth;
- mParams.top = (int) MathUtils.lerp(mParams.startPosition[1],
- app.position.y, progress);
- mParams.bottom = (int) MathUtils.lerp(mParams.startPosition[1]
- + notificationHeight,
- app.position.y + app.sourceContainerBounds.bottom,
- progress);
- applyParamsToWindow(app);
- applyParamsToNotification(mParams);
- applyParamsToNotificationList(mParams);
- }
- });
- anim.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- setExpandAnimationRunning(false);
- if (mInstantCollapsePanel) {
- mStatusBar.collapsePanel(false /* animate */);
- }
- try {
- iRemoteAnimationFinishedCallback.onAnimationFinished();
- } catch (RemoteException e) {
- e.printStackTrace();
- }
- }
- });
- anim.start();
- break;
}
}
+ int targetWidth = primary.sourceContainerBounds.width();
+ int notificationHeight = mSourceNotification.getActualHeight()
+ - mSourceNotification.getClipBottomAmount();
+ int notificationWidth = mSourceNotification.getWidth();
+ anim.setDuration(ANIMATION_DURATION);
+ anim.setInterpolator(Interpolators.LINEAR);
+ anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ mParams.linearProgress = animation.getAnimatedFraction();
+ float progress
+ = Interpolators.FAST_OUT_SLOW_IN.getInterpolation(
+ mParams.linearProgress);
+ int newWidth = (int) MathUtils.lerp(notificationWidth,
+ targetWidth, progress);
+ mParams.left = (int) ((targetWidth - newWidth) / 2.0f);
+ mParams.right = mParams.left + newWidth;
+ mParams.top = (int) MathUtils.lerp(mParams.startPosition[1],
+ primary.position.y, progress);
+ mParams.bottom = (int) MathUtils.lerp(mParams.startPosition[1]
+ + notificationHeight,
+ primary.position.y + primary.sourceContainerBounds.bottom,
+ progress);
+ applyParamsToWindow(primary);
+ applyParamsToNotification(mParams);
+ applyParamsToNotificationList(mParams);
+ }
+ });
+ anim.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ setExpandAnimationRunning(false);
+ if (mInstantCollapsePanel) {
+ mStatusBar.collapsePanel(false /* animate */);
+ }
+ invokeCallback(iRemoteAnimationFinishedCallback);
+ }
+ });
+ anim.start();
setAnimationPending(false);
});
}
+ private void invokeCallback(IRemoteAnimationFinishedCallback callback) {
+ try {
+ callback.onAnimationFinished();
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ }
+ }
+
+ private RemoteAnimationTarget getPrimaryRemoteAnimationTarget(
+ RemoteAnimationTarget[] remoteAnimationTargets) {
+ RemoteAnimationTarget primary = null;
+ for (RemoteAnimationTarget app : remoteAnimationTargets) {
+ if (app.mode == RemoteAnimationTarget.MODE_OPENING) {
+ primary = app;
+ break;
+ }
+ }
+ return primary;
+ }
+
private void setExpandAnimationRunning(boolean running) {
mNotificationPanel.setLaunchingNotification(running);
mSourceNotification.setExpandAnimationRunning(running);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
index 66176b3..2ddae74 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarFragment.java
@@ -281,6 +281,7 @@
IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_OFF);
filter.addAction(Intent.ACTION_SCREEN_ON);
+ filter.addAction(Intent.ACTION_USER_SWITCHED);
getContext().registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, filter, null, null);
notifyNavigationBarScreenOn();
mOverviewProxyService.addCallback(mOverviewProxyListener);
@@ -521,7 +522,7 @@
// Clear any pending suggestion flag as it has either been nullified or is being shown
mPendingRotationSuggestion = false;
- getView().removeCallbacks(mCancelPendingRotationProposal);
+ if (getView() != null) getView().removeCallbacks(mCancelPendingRotationProposal);
// Handle the visibility change and animation
if (visible) { // Appear and change (cannot force)
@@ -1084,6 +1085,10 @@
|| Intent.ACTION_SCREEN_ON.equals(action)) {
notifyNavigationBarScreenOn();
}
+ if (Intent.ACTION_USER_SWITCHED.equals(action)) {
+ // The accessibility settings may be different for the new user
+ updateAccessibilityServicesState(mAccessibilityManager);
+ };
}
};
@@ -1141,6 +1146,7 @@
private final Runnable mRipple = new Runnable() {
@Override
public void run() { // Cause the ripple to fire via false presses
+ if (!mRoot.isAttachedToWindow()) return;
mRoot.setPressed(true);
mRoot.setPressed(false);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java
index ff5d0e3..6b0ac94 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QuickStepController.java
@@ -34,7 +34,6 @@
import android.util.Slog;
import android.view.MotionEvent;
import android.view.View;
-import android.view.ViewConfiguration;
import android.view.WindowManagerGlobal;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.Interpolator;
@@ -45,16 +44,13 @@
import com.android.systemui.plugins.statusbar.phone.NavGesture.GestureHelper;
import com.android.systemui.shared.recents.IOverviewProxy;
import com.android.systemui.shared.recents.utilities.Utilities;
+import com.android.systemui.shared.system.NavigationBarCompat;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_LEFT;
import static android.view.WindowManagerPolicyConstants.NAV_BAR_BOTTOM;
import static com.android.systemui.OverviewProxyService.DEBUG_OVERVIEW_PROXY;
import static com.android.systemui.OverviewProxyService.TAG_OPS;
import static com.android.systemui.shared.system.NavigationBarCompat.HIT_TARGET_HOME;
-import static com.android.systemui.shared.system.NavigationBarCompat.QUICK_SCRUB_DRAG_SLOP_PX;
-import static com.android.systemui.shared.system.NavigationBarCompat.QUICK_SCRUB_TOUCH_SLOP_PX;
-import static com.android.systemui.shared.system.NavigationBarCompat.QUICK_STEP_DRAG_SLOP_PX;
-import static com.android.systemui.shared.system.NavigationBarCompat.QUICK_STEP_TOUCH_SLOP_PX;
/**
* Class to detect gestures on the navigation bar and implement quick scrub.
@@ -215,17 +211,23 @@
int pos, touchDown, offset, trackSize;
if (mIsVertical) {
- exceededScrubTouchSlop = yDiff > QUICK_SCRUB_TOUCH_SLOP_PX && yDiff > xDiff;
- exceededSwipeUpTouchSlop = xDiff > QUICK_STEP_TOUCH_SLOP_PX && xDiff > yDiff;
- exceededScrubDragSlop = yDiff > QUICK_SCRUB_DRAG_SLOP_PX && yDiff > xDiff;
+ exceededScrubTouchSlop =
+ yDiff > NavigationBarCompat.getQuickScrubTouchSlopPx() && yDiff > xDiff;
+ exceededSwipeUpTouchSlop =
+ xDiff > NavigationBarCompat.getQuickStepTouchSlopPx() && xDiff > yDiff;
+ exceededScrubDragSlop =
+ yDiff > NavigationBarCompat.getQuickScrubDragSlopPx() && yDiff > xDiff;
pos = y;
touchDown = mTouchDownY;
offset = pos - mTrackRect.top;
trackSize = mTrackRect.height();
} else {
- exceededScrubTouchSlop = xDiff > QUICK_SCRUB_TOUCH_SLOP_PX && xDiff > yDiff;
- exceededSwipeUpTouchSlop = yDiff > QUICK_STEP_TOUCH_SLOP_PX && yDiff > xDiff;
- exceededScrubDragSlop = xDiff > QUICK_SCRUB_DRAG_SLOP_PX && xDiff > yDiff;
+ exceededScrubTouchSlop =
+ xDiff > NavigationBarCompat.getQuickScrubTouchSlopPx() && xDiff > yDiff;
+ exceededSwipeUpTouchSlop =
+ yDiff > NavigationBarCompat.getQuickStepTouchSlopPx() && yDiff > xDiff;
+ exceededScrubDragSlop =
+ xDiff > NavigationBarCompat.getQuickScrubDragSlopPx() && xDiff > yDiff;
pos = x;
touchDown = mTouchDownX;
offset = pos - mTrackRect.left;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java
index b4e7575..24a5896 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconControllerImpl.java
@@ -88,7 +88,7 @@
List<Slot> allSlots = getSlots();
for (int i = 0; i < allSlots.size(); i++) {
Slot slot = allSlots.get(i);
- List<StatusBarIconHolder> holders = slot.getHolderList();
+ List<StatusBarIconHolder> holders = slot.getHolderListInViewOrder();
boolean blocked = mIconBlacklist.contains(slot.getName());
for (StatusBarIconHolder holder : holders) {
@@ -121,7 +121,7 @@
// Remove all the icons.
for (int i = currentSlots.size() - 1; i >= 0; i--) {
Slot s = currentSlots.get(i);
- slotsToReAdd.put(s, s.getHolderList());
+ slotsToReAdd.put(s, s.getHolderListInViewOrder());
removeAllIconsForSlot(s.getName());
}
@@ -281,7 +281,7 @@
mIconLogger.onIconHidden(slotName);
int slotIndex = getSlotIndex(slotName);
- List<StatusBarIconHolder> iconsToRemove = slot.getHolderList();
+ List<StatusBarIconHolder> iconsToRemove = slot.getHolderListInViewOrder();
for (StatusBarIconHolder holder : iconsToRemove) {
int viewIndex = getViewIndex(slotIndex, holder.getTag());
slot.removeForTag(holder.getTag());
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconHolder.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconHolder.java
index e854dd0..c4ff85f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconHolder.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconHolder.java
@@ -110,6 +110,10 @@
}
public void setVisible(boolean visible) {
+ if (isVisible() == visible) {
+ return;
+ }
+
switch (mType) {
case TYPE_ICON:
mIcon.visible = visible;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconList.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconList.java
index c773170..b7e1cfb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconList.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarIconList.java
@@ -211,7 +211,7 @@
}
/**
- * View index is backwards from regular index
+ * View index is inverted from regular index, because they are laid out back-to-front
* @param tag the tag of the holder being viewed
* @return (1 + mSubSlots.size() - indexOfTag)
*/
@@ -228,14 +228,22 @@
return subSlots - getIndexForTag(tag) - 1;
}
- public List<StatusBarIconHolder> getHolderList() {
+ /**
+ * Build a list of the {@link StatusBarIconHolder}s in the same order they appear in their
+ * view group. This provides a safe list that can be iterated and inserted into its group.
+ *
+ * @return all holders contained here, in view order
+ */
+ public List<StatusBarIconHolder> getHolderListInViewOrder() {
ArrayList<StatusBarIconHolder> holders = new ArrayList<>();
- if (mHolder != null) {
- holders.add(mHolder);
+ if (mSubSlots != null) {
+ for (int i = mSubSlots.size() - 1; i >= 0; i--) {
+ holders.add(mSubSlots.get(i));
+ }
}
- if (mSubSlots != null) {
- holders.addAll(mSubSlots);
+ if (mHolder != null) {
+ holders.add(mHolder);
}
return holders;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
index 669a8c8..7cd433a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarSignalPolicy.java
@@ -388,6 +388,12 @@
typeContentDescription);
}
+ public MobileIconState copy() {
+ MobileIconState copy = new MobileIconState(this.subId);
+ copyTo(copy);
+ return copy;
+ }
+
public void copyTo(MobileIconState other) {
super.copyTo(other);
other.subId = subId;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
index 44e87ff..8df51db 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BluetoothControllerImpl.java
@@ -234,6 +234,7 @@
mEnabled = bluetoothState == BluetoothAdapter.STATE_ON
|| bluetoothState == BluetoothAdapter.STATE_TURNING_ON;
mState = bluetoothState;
+ updateConnected();
mHandler.sendEmptyMessage(H.MSG_STATE_CHANGED);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
index 22a48f0..85cfde8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/KeyButtonView.java
@@ -49,12 +49,11 @@
import com.android.systemui.R;
import com.android.systemui.plugins.statusbar.phone.NavBarButtonProvider.ButtonInterface;
import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.shared.system.NavigationBarCompat;
import static android.view.KeyEvent.KEYCODE_HOME;
import static android.view.accessibility.AccessibilityNodeInfo.ACTION_CLICK;
import static android.view.accessibility.AccessibilityNodeInfo.ACTION_LONG_CLICK;
-import static com.android.systemui.shared.system.NavigationBarCompat.QUICK_SCRUB_TOUCH_SLOP_PX;
-import static com.android.systemui.shared.system.NavigationBarCompat.QUICK_STEP_TOUCH_SLOP_PX;
public class KeyButtonView extends ImageView implements ButtonInterface {
private static final String TAG = KeyButtonView.class.getSimpleName();
@@ -234,10 +233,12 @@
x = (int)ev.getRawX();
y = (int)ev.getRawY();
- boolean exceededTouchSlopX = Math.abs(x - mTouchDownX) >
- (mIsVertical ? QUICK_SCRUB_TOUCH_SLOP_PX : QUICK_STEP_TOUCH_SLOP_PX);
- boolean exceededTouchSlopY = Math.abs(y - mTouchDownY) >
- (mIsVertical ? QUICK_STEP_TOUCH_SLOP_PX : QUICK_SCRUB_TOUCH_SLOP_PX);
+ boolean exceededTouchSlopX = Math.abs(x - mTouchDownX) > (mIsVertical
+ ? NavigationBarCompat.getQuickScrubTouchSlopPx()
+ : NavigationBarCompat.getQuickStepTouchSlopPx());
+ boolean exceededTouchSlopY = Math.abs(y - mTouchDownY) > (mIsVertical
+ ? NavigationBarCompat.getQuickStepTouchSlopPx()
+ : NavigationBarCompat.getQuickScrubTouchSlopPx());
if (exceededTouchSlopX || exceededTouchSlopY) {
// When quick step is enabled, prevent animating the ripple triggered by
// setPressed and decide to run it on touch up
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index 5c7ce59..d7aedc4 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -53,7 +53,6 @@
import android.os.Message;
import android.os.SystemClock;
import android.os.VibrationEffect;
-import android.os.Vibrator;
import android.provider.Settings;
import android.provider.Settings.Global;
import android.text.InputFilter;
@@ -73,8 +72,8 @@
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager.AccessibilityServicesStateChangeListener;
import android.view.animation.DecelerateInterpolator;
+import android.widget.FrameLayout;
import android.widget.ImageButton;
-import android.widget.ImageView;
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;
import android.widget.TextView;
@@ -122,7 +121,7 @@
private ImageButton mRingerIcon;
private View mSettingsView;
private ImageButton mSettingsIcon;
- private ImageView mZenIcon;
+ private FrameLayout mZenIcon;
private final List<VolumeRow> mRows = new ArrayList<>();
private ConfigurableTexts mConfigurableTexts;
private final SparseBooleanArray mDynamic = new SparseBooleanArray();
@@ -132,7 +131,6 @@
private final Accessibility mAccessibility = new Accessibility();
private final ColorStateList mActiveTint;
private final ColorStateList mInactiveTint;
- private final Vibrator mVibrator;
private boolean mShowing;
private boolean mShowA11yStream;
@@ -153,7 +151,6 @@
mActiveTint = ColorStateList.valueOf(Utils.getColorAccent(mContext));
mInactiveTint = loadColorStateList(R.color.volume_slider_inactive);
mDeviceProvisionedController = Dependency.get(DeviceProvisionedController.class);
- mVibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
}
public void init(int windowType, Callback callback) {
@@ -673,7 +670,28 @@
* @param enable whether to enable volume row views and hide dnd icon
*/
private void enableVolumeRowViewsH(VolumeRow row, boolean enable) {
- row.dndIcon.setVisibility(enable ? GONE : VISIBLE);
+ boolean showDndIcon = !enable;
+ row.dndIcon.setVisibility(showDndIcon ? VISIBLE : GONE);
+
+ if (showDndIcon && getNumVisibleRows() == 1) {
+ row.dndIcon.setLayoutParams(new FrameLayout.LayoutParams(
+ mContext.getResources().getDimensionPixelSize(
+ R.dimen.volume_dialog_panel_width),
+ FrameLayout.LayoutParams.WRAP_CONTENT));
+ } else if (row.view.getVisibility() == VISIBLE) {
+ row.dndIcon.setLayoutParams(new FrameLayout.LayoutParams(
+ FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.WRAP_CONTENT));
+ }
+ }
+
+ private int getNumVisibleRows() {
+ int count = 0;
+ for (int i = 0; i < mRows.size(); i++) {
+ if (mRows.get(i).view.getVisibility() == VISIBLE) {
+ count++;
+ }
+ }
+ return count;
}
/**
@@ -1241,6 +1259,6 @@
private ObjectAnimator anim; // slider progress animation for non-touch-related updates
private int animTargetProgress;
private int lastAudibleLevel = 1;
- private ImageView dndIcon;
+ private FrameLayout dndIcon;
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconListTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconListTest.java
index 07ac11b..e529e4c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconListTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconListTest.java
@@ -132,7 +132,7 @@
*/
@Test
- public void testSlot_OrderIsPreserved() {
+ public void testSlot_ViewOrder() {
Slot testSlot = new Slot("test_name", null);
// no tag bc it defaults to 0
@@ -144,17 +144,18 @@
int sb3Tag = 2;
when(sbHolder3.getTag()).thenReturn(sb3Tag);
- ArrayList<StatusBarIconHolder> expected = new ArrayList<>();
- expected.add(sbHolder1);
- expected.add(sbHolder2);
- expected.add(sbHolder3);
-
-
// Add 3 icons in the same slot, and verify that the list we get is equal to what we gave
- for (StatusBarIconHolder holder : expected) {
- testSlot.addHolder(holder);
- }
- assertTrue(listsEqual(expected, testSlot.getHolderList()));
+ testSlot.addHolder(sbHolder1);
+ testSlot.addHolder(sbHolder2);
+ testSlot.addHolder(sbHolder3);
+
+ // View order is reverse of the order added
+ ArrayList<StatusBarIconHolder> expected = new ArrayList<>();
+ expected.add(sbHolder3);
+ expected.add(sbHolder2);
+ expected.add(sbHolder1);
+
+ assertTrue(listsEqual(expected, testSlot.getHolderListInViewOrder()));
}
private boolean listsEqual(List<StatusBarIconHolder> list1, List<StatusBarIconHolder> list2) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BluetoothControllerImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BluetoothControllerImplTest.java
index 54153a7..d246350 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BluetoothControllerImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BluetoothControllerImplTest.java
@@ -16,6 +16,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertFalse;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -162,4 +163,38 @@
mainLooper.destroy();
}
}
+
+ @Test
+ public void testOnServiceConnected_updatesConnectionState() {
+ when(mMockAdapter.getConnectionState()).thenReturn(BluetoothAdapter.STATE_CONNECTING);
+
+ mBluetoothControllerImpl.onServiceConnected();
+
+ assertTrue(mBluetoothControllerImpl.isBluetoothConnecting());
+ assertFalse(mBluetoothControllerImpl.isBluetoothConnected());
+ }
+
+ @Test
+ public void testOnBluetoothStateChange_updatesBluetoothState() {
+ mBluetoothControllerImpl.onBluetoothStateChanged(BluetoothAdapter.STATE_OFF);
+
+ assertEquals(BluetoothAdapter.STATE_OFF, mBluetoothControllerImpl.getBluetoothState());
+
+ mBluetoothControllerImpl.onBluetoothStateChanged(BluetoothAdapter.STATE_ON);
+
+ assertEquals(BluetoothAdapter.STATE_ON, mBluetoothControllerImpl.getBluetoothState());
+ }
+
+ @Test
+ public void testOnBluetoothStateChange_updatesConnectionState() {
+ when(mMockAdapter.getConnectionState()).thenReturn(
+ BluetoothAdapter.STATE_CONNECTING,
+ BluetoothAdapter.STATE_DISCONNECTED);
+
+ mBluetoothControllerImpl.onServiceConnected();
+ mBluetoothControllerImpl.onBluetoothStateChanged(BluetoothAdapter.STATE_OFF);
+
+ assertFalse(mBluetoothControllerImpl.isBluetoothConnecting());
+ assertFalse(mBluetoothControllerImpl.isBluetoothConnected());
+ }
}
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 08aa063..7798cf7 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -2232,7 +2232,7 @@
}
if (service != null) {
mFingerprintGestureDispatcher = new FingerprintGestureDispatcher(
- service, mLock);
+ service, mContext.getResources(), mLock);
break;
}
}
diff --git a/services/accessibility/java/com/android/server/accessibility/FingerprintGestureDispatcher.java b/services/accessibility/java/com/android/server/accessibility/FingerprintGestureDispatcher.java
index fe787b3..96418aac 100644
--- a/services/accessibility/java/com/android/server/accessibility/FingerprintGestureDispatcher.java
+++ b/services/accessibility/java/com/android/server/accessibility/FingerprintGestureDispatcher.java
@@ -17,6 +17,7 @@
package com.android.server.accessibility;
import android.accessibilityservice.FingerprintGestureController;
+import android.content.res.Resources;
import android.hardware.fingerprint.IFingerprintClientActiveCallback;
import android.hardware.fingerprint.IFingerprintService;
import android.os.Binder;
@@ -42,6 +43,7 @@
private final Object mLock;
private final IFingerprintService mFingerprintService;
private final Handler mHandler;
+ private final boolean mHardwareSupportsGestures;
// This field is ground truth for whether or not we are registered. Only write to it in handler.
private boolean mRegisteredReadOnlyExceptInHandler;
@@ -50,8 +52,11 @@
* @param fingerprintService The system's fingerprint service
* @param lock A lock to use when managing internal state
*/
- public FingerprintGestureDispatcher(IFingerprintService fingerprintService, Object lock) {
+ public FingerprintGestureDispatcher(IFingerprintService fingerprintService,
+ Resources resources, Object lock) {
mFingerprintService = fingerprintService;
+ mHardwareSupportsGestures = resources.getBoolean(
+ com.android.internal.R.bool.config_fingerprintSupportsGestures);
mLock = lock;
mHandler = new Handler(this);
}
@@ -61,9 +66,11 @@
* @param lock A lock to use when managing internal state
* @param handler A handler to use internally. Used for testing.
*/
- public FingerprintGestureDispatcher(IFingerprintService fingerprintService, Object lock,
- Handler handler) {
+ public FingerprintGestureDispatcher(IFingerprintService fingerprintService,
+ Resources resources, Object lock, Handler handler) {
mFingerprintService = fingerprintService;
+ mHardwareSupportsGestures = resources.getBoolean(
+ com.android.internal.R.bool.config_fingerprintSupportsGestures);
mLock = lock;
mHandler = handler;
}
@@ -74,6 +81,8 @@
* @param clientList The list of potential clients.
*/
public void updateClientList(List<? extends FingerprintGestureClient> clientList) {
+ if (!mHardwareSupportsGestures) return;
+
synchronized (mLock) {
mCapturingClients.clear();
for (int i = 0; i < clientList.size(); i++) {
@@ -96,6 +105,8 @@
@Override
public void onClientActiveChanged(boolean nonGestureFingerprintClientActive) {
+ if (!mHardwareSupportsGestures) return;
+
synchronized (mLock) {
for (int i = 0; i < mCapturingClients.size(); i++) {
mCapturingClients.get(i).onFingerprintGestureDetectionActiveChanged(
@@ -105,6 +116,8 @@
}
public boolean isFingerprintGestureDetectionAvailable() {
+ if (!mHardwareSupportsGestures) return false;
+
long identity = Binder.clearCallingIdentity();
try {
return !mFingerprintService.isClientActive();
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index e6b2a35..c055060 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -2079,7 +2079,8 @@
}
getUiForShowing().showFillUi(filledId, response, filterText,
- mService.getServicePackageName(), mComponentName.getPackageName(), this);
+ mService.getServicePackageName(), mComponentName.getPackageName(),
+ mService.getServiceLabel(), mService.getServiceIcon(), this);
synchronized (mLock) {
if (mUiShownTime == 0) {
diff --git a/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java b/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java
index 21a39e4..ee18dc2 100644
--- a/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java
+++ b/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java
@@ -163,11 +163,14 @@
* @param filterText text of the view to be filled
* @param servicePackageName package name of the autofill service filling the activity
* @param packageName package name of the activity that is filled
+ * @param serviceLabel label of autofill service
+ * @param serviceIcon icon of autofill service
* @param callback Identifier for the caller
*/
public void showFillUi(@NonNull AutofillId focusedId, @NonNull FillResponse response,
@Nullable String filterText, @Nullable String servicePackageName,
- @NonNull String packageName, @NonNull AutoFillUiCallback callback) {
+ @NonNull String packageName, @NonNull CharSequence serviceLabel,
+ @NonNull Drawable serviceIcon, @NonNull AutoFillUiCallback callback) {
if (sDebug) {
final int size = filterText == null ? 0 : filterText.length();
Slog.d(TAG, "showFillUi(): id=" + focusedId + ", filter=" + size + " chars");
@@ -185,7 +188,7 @@
}
hideAllUiThread(callback);
mFillUi = new FillUi(mContext, response, focusedId,
- filterText, mOverlayControl, new FillUi.Callback() {
+ filterText, mOverlayControl, serviceLabel, serviceIcon, new FillUi.Callback() {
@Override
public void onResponsePicked(FillResponse response) {
log.setType(MetricsEvent.TYPE_DETAIL);
diff --git a/services/autofill/java/com/android/server/autofill/ui/FillUi.java b/services/autofill/java/com/android/server/autofill/ui/FillUi.java
index d29ca05..1aeb3b9 100644
--- a/services/autofill/java/com/android/server/autofill/ui/FillUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/FillUi.java
@@ -26,6 +26,8 @@
import android.annotation.Nullable;
import android.app.PendingIntent;
import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.view.ContextThemeWrapper;
import android.content.Intent;
import android.content.IntentSender;
import android.content.pm.PackageManager;
@@ -53,9 +55,11 @@
import android.widget.Filter;
import android.widget.Filterable;
import android.widget.FrameLayout;
+import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.RemoteViews;
+import android.widget.TextView;
import com.android.internal.R;
import com.android.server.UiThread;
@@ -72,30 +76,10 @@
final class FillUi {
private static final String TAG = "FillUi";
+ private static final int THEME_ID = com.android.internal.R.style.Theme_DeviceDefault_Autofill;
+
private static final TypedValue sTempTypedValue = new TypedValue();
- public static final class AutofillFrameLayout extends FrameLayout {
-
- OnKeyListener mUnhandledListener;
-
- public AutofillFrameLayout(Context context, AttributeSet attrs) {
- super(context, attrs);
- }
-
- public AutofillFrameLayout(Context context, AttributeSet attrs, @AttrRes int defStyleAttr) {
- super(context, attrs, defStyleAttr);
- }
-
- @Override
- public boolean dispatchKeyEvent(KeyEvent event) {
- boolean handled = super.dispatchKeyEvent(event);
- if (!handled) {
- handled = mUnhandledListener.onKey(this, event.getKeyCode(), event);
- }
- return handled;
- }
- }
-
interface Callback {
void onResponsePicked(@NonNull FillResponse response);
void onDatasetPicked(@NonNull Dataset dataset);
@@ -146,51 +130,64 @@
FillUi(@NonNull Context context, @NonNull FillResponse response,
@NonNull AutofillId focusedViewId, @NonNull @Nullable String filterText,
- @NonNull OverlayControl overlayControl, @NonNull Callback callback) {
- mContext = context;
+ @NonNull OverlayControl overlayControl, @NonNull CharSequence serviceLabel,
+ @NonNull Drawable serviceIcon, @NonNull Callback callback) {
mCallback = callback;
mFullScreen = isFullScreen(context);
-
- final LayoutInflater inflater = LayoutInflater.from(context);
+ mContext = new ContextThemeWrapper(context, THEME_ID);
+ final LayoutInflater inflater = LayoutInflater.from(mContext);
final RemoteViews headerPresentation = response.getHeader();
final RemoteViews footerPresentation = response.getFooter();
final ViewGroup decor;
- if (headerPresentation != null || footerPresentation != null) {
- decor = (ViewGroup) inflater.inflate(
- mFullScreen ? R.layout.autofill_dataset_picker_header_footer_fullscreen
- : R.layout.autofill_dataset_picker_header_footer, null);
+ if (mFullScreen) {
+ decor = (ViewGroup) inflater.inflate(R.layout.autofill_dataset_picker_fullscreen, null);
+ } else if (headerPresentation != null || footerPresentation != null) {
+ decor = (ViewGroup) inflater.inflate(R.layout.autofill_dataset_picker_header_footer,
+ null);
} else {
- decor = (ViewGroup) inflater.inflate(
- mFullScreen ? R.layout.autofill_dataset_picker_fullscreen
- : R.layout.autofill_dataset_picker, null);
+ decor = (ViewGroup) inflater.inflate(R.layout.autofill_dataset_picker, null);
+ }
+ final TextView titleView = decor.findViewById(R.id.autofill_dataset_title);
+ if (titleView != null) {
+ titleView.setText(mContext.getString(R.string.autofill_window_title, serviceLabel));
+ }
+ final ImageView iconView = decor.findViewById(R.id.autofill_dataset_icon);
+ if (iconView != null) {
+ iconView.setImageDrawable(serviceIcon);
}
- // if autofill ui is not fullscreen, send unhandled keyevent to app window.
- if (!mFullScreen) {
- if (decor instanceof AutofillFrameLayout) {
- ((AutofillFrameLayout) decor).mUnhandledListener =
- (View view, int keyCode, KeyEvent event) -> {
- switch (keyCode) {
- case KeyEvent.KEYCODE_BACK:
- case KeyEvent.KEYCODE_ESCAPE:
- case KeyEvent.KEYCODE_ENTER:
- case KeyEvent.KEYCODE_DPAD_CENTER:
- case KeyEvent.KEYCODE_DPAD_LEFT:
- case KeyEvent.KEYCODE_DPAD_UP:
- case KeyEvent.KEYCODE_DPAD_RIGHT:
- case KeyEvent.KEYCODE_DPAD_DOWN:
- return false;
- default:
- mCallback.dispatchUnhandledKey(event);
- return true;
- }
- };
- } else {
- Slog.wtf(TAG, "Unable to send unhandled key");
+ // In full screen we only initialize size once assuming screen size never changes
+ if (mFullScreen) {
+ final Point outPoint = mTempPoint;
+ mContext.getDisplay().getSize(outPoint);
+ // full with of screen and half height of screen
+ mContentWidth = LayoutParams.MATCH_PARENT;
+ mContentHeight = outPoint.y / 2;
+ if (sVerbose) {
+ Slog.v(TAG, "initialized fillscreen LayoutParams "
+ + mContentWidth + "," + mContentHeight);
}
}
+ // Send unhandled keyevent to app window.
+ decor.addOnUnhandledKeyEventListener((View view, KeyEvent event) -> {
+ switch (event.getKeyCode() ) {
+ case KeyEvent.KEYCODE_BACK:
+ case KeyEvent.KEYCODE_ESCAPE:
+ case KeyEvent.KEYCODE_ENTER:
+ case KeyEvent.KEYCODE_DPAD_CENTER:
+ case KeyEvent.KEYCODE_DPAD_LEFT:
+ case KeyEvent.KEYCODE_DPAD_UP:
+ case KeyEvent.KEYCODE_DPAD_RIGHT:
+ case KeyEvent.KEYCODE_DPAD_DOWN:
+ return false;
+ default:
+ mCallback.dispatchUnhandledKey(event);
+ return true;
+ }
+ });
+
if (sVisibleDatasetsMaxCount > 0) {
mVisibleDatasetsMaxCount = sVisibleDatasetsMaxCount;
if (sVerbose) {
@@ -218,14 +215,12 @@
mFooter = null;
mAdapter = null;
- // insert authentication item under autofill_dataset_container or decor
- ViewGroup container = decor.findViewById(R.id.autofill_dataset_container);
- if (container == null) {
- container = decor;
- }
+ // insert authentication item under autofill_dataset_picker
+ ViewGroup container = decor.findViewById(R.id.autofill_dataset_picker);
final View content;
try {
- content = response.getPresentation().apply(context, decor, interceptionHandler);
+ response.getPresentation().setApplyTheme(THEME_ID);
+ content = response.getPresentation().apply(mContext, decor, interceptionHandler);
container.addView(content);
} catch (RuntimeException e) {
callback.onCanceled();
@@ -236,20 +231,22 @@
decor.setFocusable(true);
decor.setOnClickListener(v -> mCallback.onResponsePicked(response));
- final Point maxSize = mTempPoint;
- resolveMaxWindowSize(context, maxSize);
- // fullScreen mode occupy the full width defined by autofill_dataset_picker_max_width
- content.getLayoutParams().width = mFullScreen ? maxSize.x
- : ViewGroup.LayoutParams.WRAP_CONTENT;
- content.getLayoutParams().height = ViewGroup.LayoutParams.WRAP_CONTENT;
- final int widthMeasureSpec = MeasureSpec.makeMeasureSpec(maxSize.x,
- MeasureSpec.AT_MOST);
- final int heightMeasureSpec = MeasureSpec.makeMeasureSpec(maxSize.y,
- MeasureSpec.AT_MOST);
+ if (!mFullScreen) {
+ final Point maxSize = mTempPoint;
+ resolveMaxWindowSize(mContext, maxSize);
+ // fullScreen mode occupy the full width defined by autofill_dataset_picker_max_width
+ content.getLayoutParams().width = mFullScreen ? maxSize.x
+ : ViewGroup.LayoutParams.WRAP_CONTENT;
+ content.getLayoutParams().height = ViewGroup.LayoutParams.WRAP_CONTENT;
+ final int widthMeasureSpec = MeasureSpec.makeMeasureSpec(maxSize.x,
+ MeasureSpec.AT_MOST);
+ final int heightMeasureSpec = MeasureSpec.makeMeasureSpec(maxSize.y,
+ MeasureSpec.AT_MOST);
- decor.measure(widthMeasureSpec, heightMeasureSpec);
- mContentWidth = content.getMeasuredWidth();
- mContentHeight = content.getMeasuredHeight();
+ decor.measure(widthMeasureSpec, heightMeasureSpec);
+ mContentWidth = content.getMeasuredWidth();
+ mContentHeight = content.getMeasuredHeight();
+ }
mWindow = new AnchoredWindow(decor, overlayControl);
requestShowFillUi();
@@ -263,7 +260,8 @@
RemoteViews.OnClickHandler clickBlocker = null;
if (headerPresentation != null) {
clickBlocker = newClickBlocker();
- mHeader = headerPresentation.apply(context, null, clickBlocker);
+ headerPresentation.setApplyTheme(THEME_ID);
+ mHeader = headerPresentation.apply(mContext, null, clickBlocker);
final LinearLayout headerContainer =
decor.findViewById(R.id.autofill_dataset_header);
if (sVerbose) Slog.v(TAG, "adding header");
@@ -274,15 +272,21 @@
}
if (footerPresentation != null) {
- if (clickBlocker == null) { // already set for header
- clickBlocker = newClickBlocker();
- }
- mFooter = footerPresentation.apply(context, null, clickBlocker);
final LinearLayout footerContainer =
decor.findViewById(R.id.autofill_dataset_footer);
- if (sVerbose) Slog.v(TAG, "adding footer");
- footerContainer.addView(mFooter);
- footerContainer.setVisibility(View.VISIBLE);
+ if (footerContainer != null) {
+ if (clickBlocker == null) { // already set for header
+ clickBlocker = newClickBlocker();
+ }
+ footerPresentation.setApplyTheme(THEME_ID);
+ mFooter = footerPresentation.apply(mContext, null, clickBlocker);
+ // Footer not supported on some platform e.g. TV
+ if (sVerbose) Slog.v(TAG, "adding footer");
+ footerContainer.addView(mFooter);
+ footerContainer.setVisibility(View.VISIBLE);
+ } else {
+ mFooter = null;
+ }
} else {
mFooter = null;
}
@@ -301,7 +305,8 @@
final View view;
try {
if (sVerbose) Slog.v(TAG, "setting remote view for " + focusedViewId);
- view = presentation.apply(context, null, interceptionHandler);
+ presentation.setApplyTheme(THEME_ID);
+ view = presentation.apply(mContext, null, interceptionHandler);
} catch (RuntimeException e) {
Slog.e(TAG, "Error inflating remote views", e);
continue;
@@ -352,12 +357,7 @@
}
void requestShowFillUi() {
- if (mFullScreen) {
- mCallback.requestShowFillUi(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT,
- mWindowPresenter);
- } else {
- mCallback.requestShowFillUi(mContentWidth, mContentHeight, mWindowPresenter);
- }
+ mCallback.requestShowFillUi(mContentWidth, mContentHeight, mWindowPresenter);
}
/**
@@ -388,12 +388,6 @@
mCallback.requestHideFillUi();
} else {
if (updateContentSize()) {
- if (mFullScreen) {
- LayoutParams lp = mListView.getLayoutParams();
- lp.width = mContentWidth;
- lp.height = mContentHeight;
- mListView.setLayoutParams(lp);
- }
requestShowFillUi();
}
if (mAdapter.getCount() > mVisibleDatasetsMaxCount) {
@@ -452,6 +446,10 @@
if (mAdapter == null) {
return false;
}
+ if (mFullScreen) {
+ // always request show fill window with fixed size for fullscreen
+ return true;
+ }
boolean changed = false;
if (mAdapter.getCount() <= 0) {
if (mContentWidth != 0) {
@@ -476,11 +474,6 @@
final int heightMeasureSpec = MeasureSpec.makeMeasureSpec(maxSize.y,
MeasureSpec.AT_MOST);
final int itemCount = mAdapter.getCount();
- if (mFullScreen) {
- // fullScreen mode occupy the full width defined by autofill_dataset_picker_max_width
- changed = true;
- mContentWidth = maxSize.x;
- }
if (mHeader != null) {
mHeader.measure(widthMeasureSpec, heightMeasureSpec);
@@ -491,20 +484,9 @@
for (int i = 0; i < itemCount; i++) {
final View view = mAdapter.getItem(i).view;
view.measure(widthMeasureSpec, heightMeasureSpec);
- if (mFullScreen) {
- // for fullscreen, add up all children height until hit max height.
- final int newContentHeight = mContentHeight + view.getMeasuredHeight();
- final int clampedNewHeight = Math.min(newContentHeight, maxSize.y);
- if (clampedNewHeight != mContentHeight) {
- mContentHeight = clampedNewHeight;
- } else if (view.getMeasuredHeight() > 0) {
- break;
- }
- } else {
- changed |= updateWidth(view, maxSize);
- if (i < mVisibleDatasetsMaxCount) {
- changed |= updateHeight(view, maxSize);
- }
+ changed |= updateWidth(view, maxSize);
+ if (i < mVisibleDatasetsMaxCount) {
+ changed |= updateHeight(view, maxSize);
}
}
diff --git a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
index f96fa7c2..80903c1 100644
--- a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
@@ -42,6 +42,7 @@
import android.util.ArraySet;
import android.util.Pair;
import android.util.Slog;
+import android.view.ContextThemeWrapper;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
@@ -70,6 +71,9 @@
private static final String TAG = "AutofillSaveUi";
+ private static final int THEME_ID =
+ com.android.internal.R.style.Theme_DeviceDefault_Autofill_Save;
+
public interface OnSaveListener {
void onSave();
void onCancel(IntentSender listener);
@@ -144,6 +148,7 @@
mServicePackageName = servicePackageName;
mPackageName = packageName;
+ context = new ContextThemeWrapper(context, THEME_ID);
final LayoutInflater inflater = LayoutInflater.from(context);
final View view = inflater.inflate(R.layout.autofill_save, null);
@@ -222,7 +227,7 @@
final View yesButton = view.findViewById(R.id.autofill_save_yes);
yesButton.setOnClickListener((v) -> mListener.onSave());
- mDialog = new Dialog(context, R.style.Theme_DeviceDefault_Light_Panel);
+ mDialog = new Dialog(context, THEME_ID);
mDialog.setContentView(view);
// Dialog can be dismissed when touched outside, but the negative listener should not be
@@ -309,6 +314,7 @@
try {
// Create the remote view peer.
+ template.setApplyTheme(THEME_ID);
final View customSubtitleView = template.apply(context, null, handler);
// And apply batch updates (if any).
diff --git a/services/core/java/com/android/server/AppOpsService.java b/services/core/java/com/android/server/AppOpsService.java
index b860191..d818bd6 100644
--- a/services/core/java/com/android/server/AppOpsService.java
+++ b/services/core/java/com/android/server/AppOpsService.java
@@ -39,6 +39,7 @@
import android.os.ServiceManager;
import android.os.ShellCallback;
import android.os.ShellCommand;
+import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.storage.StorageManagerInternal;
@@ -77,14 +78,24 @@
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
+import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
+import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
+import static android.app.AppOpsManager.UID_STATE_BACKGROUND;
+import static android.app.AppOpsManager.UID_STATE_CACHED;
+import static android.app.AppOpsManager.UID_STATE_FOREGROUND;
+import static android.app.AppOpsManager.UID_STATE_FOREGROUND_SERVICE;
+import static android.app.AppOpsManager._NUM_UID_STATE;
+import static android.app.AppOpsManager.UID_STATE_PERSISTENT;
+import static android.app.AppOpsManager.UID_STATE_TOP;
+
public class AppOpsService extends IAppOpsService.Stub {
static final String TAG = "AppOps";
static final boolean DEBUG = false;
@@ -100,6 +111,64 @@
// Constant meaning that any UID should be matched when dispatching callbacks
private static final int UID_ANY = -2;
+ // Map from process states to the uid states we track.
+ private static final int[] PROCESS_STATE_TO_UID_STATE = new int[] {
+ UID_STATE_PERSISTENT, // ActivityManager.PROCESS_STATE_PERSISTENT
+ UID_STATE_PERSISTENT, // ActivityManager.PROCESS_STATE_PERSISTENT_UI
+ UID_STATE_TOP, // ActivityManager.PROCESS_STATE_TOP
+ UID_STATE_FOREGROUND_SERVICE, // ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE
+ UID_STATE_FOREGROUND, // ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE
+ UID_STATE_FOREGROUND, // ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND
+ UID_STATE_BACKGROUND, // ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND
+ UID_STATE_BACKGROUND, // ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND
+ UID_STATE_BACKGROUND, // ActivityManager.PROCESS_STATE_BACKUP
+ UID_STATE_BACKGROUND, // ActivityManager.PROCESS_STATE_SERVICE
+ UID_STATE_BACKGROUND, // ActivityManager.PROCESS_STATE_RECEIVER
+ UID_STATE_CACHED, // ActivityManager.PROCESS_STATE_TOP_SLEEPING
+ UID_STATE_CACHED, // ActivityManager.PROCESS_STATE_HEAVY_WEIGHT
+ UID_STATE_CACHED, // ActivityManager.PROCESS_STATE_HOME
+ UID_STATE_CACHED, // ActivityManager.PROCESS_STATE_LAST_ACTIVITY
+ UID_STATE_CACHED, // ActivityManager.PROCESS_STATE_CACHED_ACTIVITY
+ UID_STATE_CACHED, // ActivityManager.PROCESS_STATE_CACHED_ACTIVITY_CLIENT
+ UID_STATE_CACHED, // ActivityManager.PROCESS_STATE_CACHED_RECENT
+ UID_STATE_CACHED, // ActivityManager.PROCESS_STATE_CACHED_EMPTY
+ UID_STATE_CACHED, // ActivityManager.PROCESS_STATE_NONEXISTENT
+ };
+
+ static final String[] UID_STATE_NAMES = new String[] {
+ "pers ", // UID_STATE_PERSISTENT
+ "top ", // UID_STATE_TOP
+ "fgsvc", // UID_STATE_FOREGROUND_SERVICE
+ "fg ", // UID_STATE_FOREGROUND
+ "bg ", // UID_STATE_BACKGROUND
+ "cch ", // UID_STATE_CACHED
+ };
+
+ static final String[] UID_STATE_TIME_ATTRS = new String[] {
+ "tp", // UID_STATE_PERSISTENT
+ "tt", // UID_STATE_TOP
+ "tfs", // UID_STATE_FOREGROUND_SERVICE
+ "tf", // UID_STATE_FOREGROUND
+ "tb", // UID_STATE_BACKGROUND
+ "tc", // UID_STATE_CACHED
+ };
+
+ static final String[] UID_STATE_REJECT_ATTRS = new String[] {
+ "rp", // UID_STATE_PERSISTENT
+ "rt", // UID_STATE_TOP
+ "rfs", // UID_STATE_FOREGROUND_SERVICE
+ "rf", // UID_STATE_FOREGROUND
+ "rb", // UID_STATE_BACKGROUND
+ "rc", // UID_STATE_CACHED
+ };
+
+ static final String[] MODE_NAMES = new String[] {
+ "allow", // MODE_ALLOWED
+ "ignore", // MODE_IGNORED
+ "deny", // MODE_ERRORED
+ "default", // MODE_DEFAULT
+ };
+
Context mContext;
final AtomicFile mFile;
final Handler mHandler;
@@ -133,6 +202,8 @@
@VisibleForTesting
static final class UidState {
public final int uid;
+ public int state = UID_STATE_CACHED;
+ public int startNesting;
public ArrayMap<String, Ops> pkgOps;
public SparseIntArray opModes;
@@ -151,36 +222,51 @@
}
}
- public final static class Ops extends SparseArray<Op> {
- public final String packageName;
- public final UidState uidState;
- public final boolean isPrivileged;
+ final static class Ops extends SparseArray<Op> {
+ final String packageName;
+ final UidState uidState;
+ final boolean isPrivileged;
- public Ops(String _packageName, UidState _uidState, boolean _isPrivileged) {
+ Ops(String _packageName, UidState _uidState, boolean _isPrivileged) {
packageName = _packageName;
uidState = _uidState;
isPrivileged = _isPrivileged;
}
}
- public final static class Op {
- public final int uid;
- public final String packageName;
- public int proxyUid = -1;
- public String proxyPackageName;
- public final int op;
- public int mode;
- public int duration;
- public long time;
- public long rejectTime;
- public int nesting;
+ final static class Op {
+ final UidState uidState;
+ final int uid;
+ final String packageName;
+ final int op;
+ int proxyUid = -1;
+ String proxyPackageName;
+ int mode;
+ int duration;
+ long time[] = new long[_NUM_UID_STATE];
+ long rejectTime[] = new long[_NUM_UID_STATE];
+ int startNesting;
+ long startRealtime;
- public Op(int _uid, String _packageName, int _op) {
- uid = _uid;
+ Op(UidState _uidState, String _packageName, int _op) {
+ uidState = _uidState;
+ uid = _uidState.uid;
packageName = _packageName;
op = _op;
mode = AppOpsManager.opToDefaultMode(op);
}
+
+ boolean hasAnyTime() {
+ for (int i = 0; i < AppOpsManager._NUM_UID_STATE; i++) {
+ if (time[i] != 0) {
+ return true;
+ }
+ if (rejectTime[i] != 0) {
+ return true;
+ }
+ }
+ return false;
+ }
}
final SparseArray<ArraySet<ModeCallback>> mOpModeWatchers = new SparseArray<>();
@@ -189,13 +275,13 @@
final ArrayMap<IBinder, SparseArray<ActiveCallback>> mActiveWatchers = new ArrayMap<>();
final SparseArray<SparseArray<Restriction>> mAudioRestrictions = new SparseArray<>();
- public final class ModeCallback implements DeathRecipient {
+ final class ModeCallback implements DeathRecipient {
final IAppOpsCallback mCallback;
final int mWatchingUid;
final int mCallingUid;
final int mCallingPid;
- public ModeCallback(IAppOpsCallback callback, int watchingUid, int callingUid,
+ ModeCallback(IAppOpsCallback callback, int watchingUid, int callingUid,
int callingPid) {
mCallback = callback;
mWatchingUid = watchingUid;
@@ -222,7 +308,7 @@
return sb.toString();
}
- public void unlinkToDeath() {
+ void unlinkToDeath() {
mCallback.asBinder().unlinkToDeath(this, 0);
}
@@ -232,13 +318,13 @@
}
}
- public final class ActiveCallback implements DeathRecipient {
+ final class ActiveCallback implements DeathRecipient {
final IAppOpsActiveCallback mCallback;
final int mWatchingUid;
final int mCallingUid;
final int mCallingPid;
- public ActiveCallback(IAppOpsActiveCallback callback, int watchingUid, int callingUid,
+ ActiveCallback(IAppOpsActiveCallback callback, int watchingUid, int callingUid,
int callingPid) {
mCallback = callback;
mWatchingUid = watchingUid;
@@ -265,7 +351,7 @@
return sb.toString();
}
- public void destroy() {
+ void destroy() {
mCallback.asBinder().unlinkToDeath(this, 0);
}
@@ -277,12 +363,12 @@
final ArrayMap<IBinder, ClientState> mClients = new ArrayMap<>();
- public final class ClientState extends Binder implements DeathRecipient {
+ final class ClientState extends Binder implements DeathRecipient {
final ArrayList<Op> mStartedOps = new ArrayList<>();
final IBinder mAppToken;
final int mPid;
- public ClientState(IBinder appToken) {
+ ClientState(IBinder appToken) {
mAppToken = appToken;
mPid = Binder.getCallingPid();
// Watch only for remote processes dying
@@ -453,7 +539,7 @@
if (uid == op.uid && packageName.equals(op.packageName)) {
finishOperationLocked(op, /*finishNested*/ true);
client.mStartedOps.remove(j);
- if (op.nesting <= 0) {
+ if (op.startNesting <= 0) {
scheduleOpActiveChangedIfNeededLocked(op.op,
uid, packageName, false);
}
@@ -485,6 +571,31 @@
}
}
+ public void updateUidProcState(int uid, int procState) {
+ synchronized (this) {
+ final UidState uidState = getUidStateLocked(uid, true);
+ final int newState = PROCESS_STATE_TO_UID_STATE[procState];
+ if (uidState != null && uidState.state != newState) {
+ if (uidState.startNesting != 0) {
+ // There is some actively running operation... need to find it
+ // and appropriately update its state.
+ final long now = System.currentTimeMillis();
+ for (int i = uidState.pkgOps.size() - 1; i >= 0; i--) {
+ final Ops ops = uidState.pkgOps.valueAt(i);
+ for (int j = ops.size() - 1; j >= 0; j--) {
+ final Op op = ops.valueAt(j);
+ if (op.startNesting > 0) {
+ op.time[uidState.state] = now;
+ op.time[newState] = now;
+ }
+ }
+ }
+ }
+ uidState.state = newState;
+ }
+ }
+ }
+
public void shutdown() {
Slog.w(TAG, "Writing app ops before shutdown...");
boolean doWrite = false;
@@ -501,12 +612,16 @@
private ArrayList<AppOpsManager.OpEntry> collectOps(Ops pkgOps, int[] ops) {
ArrayList<AppOpsManager.OpEntry> resOps = null;
+ final long elapsedNow = SystemClock.elapsedRealtime();
if (ops == null) {
- resOps = new ArrayList<AppOpsManager.OpEntry>();
+ resOps = new ArrayList<>();
for (int j=0; j<pkgOps.size(); j++) {
Op curOp = pkgOps.valueAt(j);
+ long duration = curOp.duration == -1
+ ? (elapsedNow - curOp.startRealtime)
+ : curOp.duration;
resOps.add(new AppOpsManager.OpEntry(curOp.op, curOp.mode, curOp.time,
- curOp.rejectTime, curOp.duration, curOp.proxyUid,
+ curOp.rejectTime, (int) duration, curOp.proxyUid,
curOp.proxyPackageName));
}
} else {
@@ -514,10 +629,13 @@
Op curOp = pkgOps.get(ops[j]);
if (curOp != null) {
if (resOps == null) {
- resOps = new ArrayList<AppOpsManager.OpEntry>();
+ resOps = new ArrayList<>();
}
+ long duration = curOp.duration == -1
+ ? (elapsedNow - curOp.startRealtime)
+ : curOp.duration;
resOps.add(new AppOpsManager.OpEntry(curOp.op, curOp.mode, curOp.time,
- curOp.rejectTime, curOp.duration, curOp.proxyUid,
+ curOp.rejectTime, (int) duration, curOp.proxyUid,
curOp.proxyPackageName));
}
}
@@ -628,7 +746,7 @@
}
private void pruneOp(Op op, int uid, String packageName) {
- if (op.time == 0 && op.rejectTime == 0) {
+ if (!op.hasAnyTime()) {
Ops ops = getOpsRawLocked(uid, packageName, false /* edit */,
false /* uidMismatchExpected */);
if (ops != null) {
@@ -946,7 +1064,7 @@
mOpModeWatchers.get(curOp.op));
callbacks = addCallbacks(callbacks, curOp.op, curOp.uid, packageName,
mPackageModeWatchers.get(packageName));
- if (curOp.time == 0 && curOp.rejectTime == 0) {
+ if (!curOp.hasAnyTime()) {
pkgOps.removeAt(j);
}
}
@@ -1212,24 +1330,25 @@
private int noteOperationUnchecked(int code, int uid, String packageName,
int proxyUid, String proxyPackageName) {
synchronized (this) {
- Ops ops = getOpsRawLocked(uid, packageName, true /* edit */,
+ final Ops ops = getOpsRawLocked(uid, packageName, true /* edit */,
false /* uidMismatchExpected */);
if (ops == null) {
if (DEBUG) Slog.d(TAG, "noteOperation: no op for code " + code + " uid " + uid
+ " package " + packageName);
return AppOpsManager.MODE_ERRORED;
}
- Op op = getOpLocked(ops, code, true);
+ final Op op = getOpLocked(ops, code, true);
if (isOpRestrictedLocked(uid, code, packageName)) {
return AppOpsManager.MODE_IGNORED;
}
+ final UidState uidState = ops.uidState;
if (op.duration == -1) {
Slog.w(TAG, "Noting op not finished: uid " + uid + " pkg " + packageName
- + " code " + code + " time=" + op.time + " duration=" + op.duration);
+ + " code " + code + " time=" + op.time[uidState.state]
+ + " duration=" + op.duration);
}
op.duration = 0;
final int switchCode = AppOpsManager.opToSwitch(code);
- UidState uidState = ops.uidState;
// If there is a non-default per UID policy (we set UID op mode only if
// non-default) it takes over, otherwise use the per package policy.
if (uidState.opModes != null && uidState.opModes.indexOfKey(switchCode) >= 0) {
@@ -1238,7 +1357,7 @@
if (DEBUG) Slog.d(TAG, "noteOperation: reject #" + op.mode + " for code "
+ switchCode + " (" + code + ") uid " + uid + " package "
+ packageName);
- op.rejectTime = System.currentTimeMillis();
+ op.rejectTime[uidState.state] = System.currentTimeMillis();
return uidMode;
}
} else {
@@ -1247,14 +1366,14 @@
if (DEBUG) Slog.d(TAG, "noteOperation: reject #" + op.mode + " for code "
+ switchCode + " (" + code + ") uid " + uid + " package "
+ packageName);
- op.rejectTime = System.currentTimeMillis();
+ op.rejectTime[uidState.state] = System.currentTimeMillis();
return switchOp.mode;
}
}
if (DEBUG) Slog.d(TAG, "noteOperation: allowing code " + code + " uid " + uid
+ " package " + packageName);
- op.time = System.currentTimeMillis();
- op.rejectTime = 0;
+ op.time[uidState.state] = System.currentTimeMillis();
+ op.rejectTime[uidState.state] = 0;
op.proxyUid = proxyUid;
op.proxyPackageName = proxyPackageName;
return AppOpsManager.MODE_ALLOWED;
@@ -1323,19 +1442,19 @@
}
ClientState client = (ClientState)token;
synchronized (this) {
- Ops ops = getOpsRawLocked(uid, resolvedPackageName, true /* edit */,
+ final Ops ops = getOpsRawLocked(uid, resolvedPackageName, true /* edit */,
false /* uidMismatchExpected */);
if (ops == null) {
if (DEBUG) Slog.d(TAG, "startOperation: no op for code " + code + " uid " + uid
+ " package " + resolvedPackageName);
return AppOpsManager.MODE_ERRORED;
}
- Op op = getOpLocked(ops, code, true);
+ final Op op = getOpLocked(ops, code, true);
if (isOpRestrictedLocked(uid, code, resolvedPackageName)) {
return AppOpsManager.MODE_IGNORED;
}
final int switchCode = AppOpsManager.opToSwitch(code);
- UidState uidState = ops.uidState;
+ final UidState uidState = ops.uidState;
// If there is a non-default per UID policy (we set UID op mode only if
// non-default) it takes over, otherwise use the per package policy.
if (uidState.opModes != null && uidState.opModes.indexOfKey(switchCode) >= 0) {
@@ -1345,7 +1464,7 @@
if (DEBUG) Slog.d(TAG, "noteOperation: reject #" + op.mode + " for code "
+ switchCode + " (" + code + ") uid " + uid + " package "
+ resolvedPackageName);
- op.rejectTime = System.currentTimeMillis();
+ op.rejectTime[uidState.state] = System.currentTimeMillis();
return uidMode;
}
} else {
@@ -1355,19 +1474,21 @@
if (DEBUG) Slog.d(TAG, "startOperation: reject #" + op.mode + " for code "
+ switchCode + " (" + code + ") uid " + uid + " package "
+ resolvedPackageName);
- op.rejectTime = System.currentTimeMillis();
+ op.rejectTime[uidState.state] = System.currentTimeMillis();
return switchOp.mode;
}
}
if (DEBUG) Slog.d(TAG, "startOperation: allowing code " + code + " uid " + uid
+ " package " + resolvedPackageName);
- if (op.nesting == 0) {
- op.time = System.currentTimeMillis();
- op.rejectTime = 0;
+ if (op.startNesting == 0) {
+ op.startRealtime = SystemClock.elapsedRealtime();
+ op.time[uidState.state] = System.currentTimeMillis();
+ op.rejectTime[uidState.state] = 0;
op.duration = -1;
scheduleOpActiveChangedIfNeededLocked(code, uid, packageName, true);
}
- op.nesting++;
+ op.startNesting++;
+ uidState.startNesting++;
if (client.mStartedOps != null) {
client.mStartedOps.add(op);
}
@@ -1415,7 +1536,7 @@
return;
}
finishOperationLocked(op, /*finishNested*/ false);
- if (op.nesting <= 0) {
+ if (op.startNesting <= 0) {
scheduleOpActiveChangedIfNeededLocked(code, uid, packageName, false);
}
}
@@ -1476,18 +1597,22 @@
}
void finishOperationLocked(Op op, boolean finishNested) {
- if (op.nesting <= 1 || finishNested) {
- if (op.nesting == 1 || finishNested) {
- op.duration = (int)(System.currentTimeMillis() - op.time);
- op.time += op.duration;
+ if (op.startNesting <= 1 || finishNested) {
+ if (op.startNesting == 1 || finishNested) {
+ op.duration = (int)(SystemClock.elapsedRealtime() - op.startRealtime);
+ op.time[op.uidState.state] = System.currentTimeMillis();
} else {
Slog.w(TAG, "Finishing op nesting under-run: uid " + op.uid + " pkg "
+ op.packageName + " code " + op.op + " time=" + op.time
- + " duration=" + op.duration + " nesting=" + op.nesting);
+ + " duration=" + op.duration + " nesting=" + op.startNesting);
}
- op.nesting = 0;
+ if (op.startNesting >= 1) {
+ op.uidState.startNesting -= op.startNesting;
+ }
+ op.startNesting = 0;
} else {
- op.nesting--;
+ op.startNesting--;
+ op.uidState.startNesting--;
}
}
@@ -1617,7 +1742,7 @@
if (!edit) {
return null;
}
- op = new Op(ops.uidState.uid, ops.packageName, code);
+ op = new Op(ops.uidState, ops.packageName, code);
ops.put(code, op);
}
if (edit) {
@@ -1750,7 +1875,7 @@
if (ops != null) {
final Op op = ops.get(AppOpsManager.OP_RUN_IN_BACKGROUND);
if (op != null && op.mode != AppOpsManager.opToDefaultMode(op.op)) {
- final Op copy = new Op(op.uid, op.packageName,
+ final Op copy = new Op(op.uidState, op.packageName,
AppOpsManager.OP_RUN_ANY_IN_BACKGROUND);
copy.mode = op.mode;
ops.put(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, copy);
@@ -1860,37 +1985,86 @@
String tagName = parser.getName();
if (tagName.equals("op")) {
- Op op = new Op(uid, pkgName, Integer.parseInt(parser.getAttributeValue(null, "n")));
- String mode = parser.getAttributeValue(null, "m");
- if (mode != null) {
- op.mode = Integer.parseInt(mode);
- }
- String time = parser.getAttributeValue(null, "t");
- if (time != null) {
- op.time = Long.parseLong(time);
- }
- time = parser.getAttributeValue(null, "r");
- if (time != null) {
- op.rejectTime = Long.parseLong(time);
- }
- String dur = parser.getAttributeValue(null, "d");
- if (dur != null) {
- op.duration = Integer.parseInt(dur);
- }
- String proxyUid = parser.getAttributeValue(null, "pu");
- if (proxyUid != null) {
- op.proxyUid = Integer.parseInt(proxyUid);
- }
- String proxyPackageName = parser.getAttributeValue(null, "pp");
- if (proxyPackageName != null) {
- op.proxyPackageName = proxyPackageName;
- }
-
UidState uidState = getUidStateLocked(uid, true);
if (uidState.pkgOps == null) {
uidState.pkgOps = new ArrayMap<>();
}
+ Op op = new Op(uidState, pkgName,
+ Integer.parseInt(parser.getAttributeValue(null, "n")));
+
+ for (int i = parser.getAttributeCount()-1; i >= 0; i--) {
+ final String name = parser.getAttributeName(i);
+ final String value = parser.getAttributeValue(i);
+ switch (name) {
+ case "m":
+ op.mode = Integer.parseInt(value);
+ break;
+ case "d":
+ op.duration = Integer.parseInt(value);
+ break;
+ case "pu":
+ op.proxyUid = Integer.parseInt(value);
+ break;
+ case "pp":
+ op.proxyPackageName = value;
+ break;
+ case "tp":
+ op.time[AppOpsManager.UID_STATE_PERSISTENT] = Long.parseLong(value);
+ break;
+ case "tt":
+ op.time[AppOpsManager.UID_STATE_TOP] = Long.parseLong(value);
+ break;
+ case "tfs":
+ op.time[AppOpsManager.UID_STATE_FOREGROUND_SERVICE]
+ = Long.parseLong(value);
+ break;
+ case "tf":
+ op.time[AppOpsManager.UID_STATE_FOREGROUND] = Long.parseLong(value);
+ break;
+ case "tb":
+ op.time[AppOpsManager.UID_STATE_BACKGROUND] = Long.parseLong(value);
+ break;
+ case "tc":
+ op.time[AppOpsManager.UID_STATE_CACHED] = Long.parseLong(value);
+ break;
+ case "rp":
+ op.rejectTime[AppOpsManager.UID_STATE_PERSISTENT]
+ = Long.parseLong(value);
+ break;
+ case "rt":
+ op.rejectTime[AppOpsManager.UID_STATE_TOP] = Long.parseLong(value);
+ break;
+ case "rfs":
+ op.rejectTime[AppOpsManager.UID_STATE_FOREGROUND_SERVICE]
+ = Long.parseLong(value);
+ break;
+ case "rf":
+ op.rejectTime[AppOpsManager.UID_STATE_FOREGROUND]
+ = Long.parseLong(value);
+ break;
+ case "rb":
+ op.rejectTime[AppOpsManager.UID_STATE_BACKGROUND]
+ = Long.parseLong(value);
+ break;
+ case "rc":
+ op.rejectTime[AppOpsManager.UID_STATE_CACHED]
+ = Long.parseLong(value);
+ break;
+ case "t":
+ // Backwards compat.
+ op.time[AppOpsManager.UID_STATE_TOP] = Long.parseLong(value);
+ break;
+ case "r":
+ // Backwards compat.
+ op.rejectTime[AppOpsManager.UID_STATE_TOP] = Long.parseLong(value);
+ break;
+ default:
+ Slog.w(TAG, "Unknown attribute in 'op' tag: " + name);
+ break;
+ }
+ }
+
Ops ops = uidState.pkgOps.get(pkgName);
if (ops == null) {
ops = new Ops(pkgName, uidState, isPrivileged);
@@ -1977,13 +2151,17 @@
if (op.getMode() != AppOpsManager.opToDefaultMode(op.getOp())) {
out.attribute(null, "m", Integer.toString(op.getMode()));
}
- long time = op.getTime();
- if (time != 0) {
- out.attribute(null, "t", Long.toString(time));
- }
- time = op.getRejectTime();
- if (time != 0) {
- out.attribute(null, "r", Long.toString(time));
+ for (int k = 0; k < _NUM_UID_STATE; k++) {
+ final long time = op.getTimeFor(k);
+ if (time != 0) {
+ out.attribute(null, UID_STATE_TIME_ATTRS[k],
+ Long.toString(time));
+ }
+ final long rejectTime = op.getRejectTimeFor(k);
+ if (rejectTime != 0) {
+ out.attribute(null, UID_STATE_REJECT_ATTRS[k],
+ Long.toString(rejectTime));
+ }
}
int dur = op.getDuration();
if (dur != 0) {
@@ -2069,15 +2247,10 @@
}
int strModeToMode(String modeStr, PrintWriter err) {
- switch (modeStr) {
- case "allow":
- return AppOpsManager.MODE_ALLOWED;
- case "deny":
- return AppOpsManager.MODE_ERRORED;
- case "ignore":
- return AppOpsManager.MODE_IGNORED;
- case "default":
- return AppOpsManager.MODE_DEFAULT;
+ for (int i = MODE_NAMES.length - 1; i >= 0; i--) {
+ if (MODE_NAMES[i].equals(modeStr)) {
+ return i;
+ }
}
try {
return Integer.parseInt(modeStr);
@@ -2466,6 +2639,34 @@
pw.println(" none");
}
+ private void dumpTimesLocked(PrintWriter pw, String firstPrefix, String prefix, long[] times,
+ long now, SimpleDateFormat sdf, Date date) {
+ boolean hasTime = false;
+ for (int i = 0; i < _NUM_UID_STATE; i++) {
+ if (times[i] != 0) {
+ hasTime = true;
+ break;
+ }
+ }
+ if (!hasTime) {
+ return;
+ }
+ boolean first = true;
+ for (int i = 0; i < _NUM_UID_STATE; i++) {
+ if (times[i] != 0) {
+ pw.print(first ? firstPrefix : prefix);
+ first = false;
+ pw.print(UID_STATE_NAMES[i]);
+ pw.print(" = ");
+ date.setTime(times[i]);
+ pw.print(sdf.format(date));
+ pw.print(" (");
+ TimeUtils.formatDuration(times[i]-now, pw);
+ pw.println(")");
+ }
+ }
+ }
+
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, TAG, pw)) return;
@@ -2491,6 +2692,9 @@
synchronized (this) {
pw.println("Current AppOps Service state:");
final long now = System.currentTimeMillis();
+ final long nowElapsed = SystemClock.elapsedRealtime();
+ final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
+ final Date date = new Date();
boolean needSep = false;
if (mOpModeWatchers.size() > 0) {
needSep = true;
@@ -2585,7 +2789,7 @@
pw.print(" "); pw.print(op);
pw.print(" usage="); pw.print(AudioAttributes.usageToString(usage));
Restriction r = restrictions.valueAt(i);
- pw.print(": mode="); pw.println(r.mode);
+ pw.print(": mode="); pw.println(MODE_NAMES[r.mode]);
if (!r.exceptionPackages.isEmpty()) {
pw.println(" Exceptions:");
for (int j=0; j<r.exceptionPackages.size(); j++) {
@@ -2602,6 +2806,12 @@
UidState uidState = mUidStates.valueAt(i);
pw.print(" Uid "); UserHandle.formatUid(pw, uidState.uid); pw.println(":");
+ pw.print(" state=");
+ pw.println(UID_STATE_NAMES[uidState.state]);
+ if (uidState.startNesting != 0) {
+ pw.print(" startNesting=");
+ pw.println(uidState.startNesting);
+ }
needSep = true;
SparseIntArray opModes = uidState.opModes;
@@ -2611,7 +2821,7 @@
final int code = opModes.keyAt(j);
final int mode = opModes.valueAt(j);
pw.print(" "); pw.print(AppOpsManager.opToName(code));
- pw.print(": mode="); pw.println(mode);
+ pw.print(": mode="); pw.println(MODE_NAMES[mode]);
}
}
@@ -2625,21 +2835,26 @@
for (int j=0; j<ops.size(); j++) {
Op op = ops.valueAt(j);
pw.print(" "); pw.print(AppOpsManager.opToName(op.op));
- pw.print(": mode="); pw.print(op.mode);
- if (op.time != 0) {
- pw.print("; time="); TimeUtils.formatDuration(now-op.time, pw);
- pw.print(" ago");
- }
- if (op.rejectTime != 0) {
- pw.print("; rejectTime="); TimeUtils.formatDuration(now-op.rejectTime, pw);
- pw.print(" ago");
- }
+ pw.print(" ("); pw.print(MODE_NAMES[op.mode]); pw.println("): ");
+ dumpTimesLocked(pw,
+ " Access: ",
+ " ", op.time, now, sdf, date);
+ dumpTimesLocked(pw,
+ " Reject: ",
+ " ", op.rejectTime, now, sdf, date);
if (op.duration == -1) {
- pw.print(" (running)");
+ pw.print(" Running start at: ");
+ TimeUtils.formatDuration(nowElapsed-op.startRealtime, pw);
+ pw.println();
} else if (op.duration != 0) {
- pw.print("; duration="); TimeUtils.formatDuration(op.duration, pw);
+ pw.print(" duration=");
+ TimeUtils.formatDuration(op.duration, pw);
+ pw.println();
}
- pw.println();
+ if (op.startNesting != 0) {
+ pw.print(" startNesting=");
+ pw.println(op.startNesting);
+ }
}
}
}
diff --git a/services/core/java/com/android/server/BinderCallsStatsService.java b/services/core/java/com/android/server/BinderCallsStatsService.java
index ae14dfa..1cd853f 100644
--- a/services/core/java/com/android/server/BinderCallsStatsService.java
+++ b/services/core/java/com/android/server/BinderCallsStatsService.java
@@ -56,7 +56,10 @@
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (args != null) {
for (final String arg : args) {
- if ("--reset".equals(arg)) {
+ if ("-a".equals(arg)) {
+ // We currently dump all information by default
+ continue;
+ } else if ("--reset".equals(arg)) {
reset();
pw.println("binder_calls_stats reset.");
return;
@@ -78,7 +81,6 @@
return;
} else {
pw.println("Unknown option: " + arg);
- return;
}
}
}
diff --git a/services/core/java/com/android/server/IpSecService.java b/services/core/java/com/android/server/IpSecService.java
index cd90e3f..33ca02f 100644
--- a/services/core/java/com/android/server/IpSecService.java
+++ b/services/core/java/com/android/server/IpSecService.java
@@ -1101,9 +1101,11 @@
new RefcountedResource<SpiRecord>(
new SpiRecord(resourceId, "", destinationAddress, spi), binder));
} catch (ServiceSpecificException e) {
- // TODO: Add appropriate checks when other ServiceSpecificException types are supported
- return new IpSecSpiResponse(
- IpSecManager.Status.SPI_UNAVAILABLE, INVALID_RESOURCE_ID, spi);
+ if (e.errorCode == OsConstants.ENOENT) {
+ return new IpSecSpiResponse(
+ IpSecManager.Status.SPI_UNAVAILABLE, INVALID_RESOURCE_ID, spi);
+ }
+ throw e;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -1115,7 +1117,6 @@
*/
private void releaseResource(RefcountedResourceArray resArray, int resourceId)
throws RemoteException {
-
resArray.getRefcountedResourceOrThrow(resourceId).userRelease();
}
@@ -1315,15 +1316,12 @@
releaseNetId(ikey);
releaseNetId(okey);
throw e.rethrowFromSystemServer();
- } catch (ServiceSpecificException e) {
- // FIXME: get the error code and throw is at an IOException from Errno Exception
+ } catch (Throwable t) {
+ // Release keys if we got an error.
+ releaseNetId(ikey);
+ releaseNetId(okey);
+ throw t;
}
-
- // If we make it to here, then something has gone wrong and we couldn't create a VTI.
- // Release the keys that we reserved, and return an error status.
- releaseNetId(ikey);
- releaseNetId(okey);
- return new IpSecTunnelInterfaceResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
}
/**
@@ -1352,9 +1350,6 @@
localAddr.getPrefixLength());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
- } catch (ServiceSpecificException e) {
- // If we get here, one of the arguments provided was invalid. Wrap the SSE, and throw.
- throw new IllegalArgumentException(e);
}
}
@@ -1384,9 +1379,6 @@
localAddr.getPrefixLength());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
- } catch (ServiceSpecificException e) {
- // If we get here, one of the arguments provided was invalid. Wrap the SSE, and throw.
- throw new IllegalArgumentException(e);
}
}
@@ -1590,12 +1582,7 @@
dependencies.add(refcountedSpiRecord);
SpiRecord spiRecord = refcountedSpiRecord.getResource();
- try {
- createOrUpdateTransform(c, resourceId, spiRecord, socketRecord);
- } catch (ServiceSpecificException e) {
- // FIXME: get the error code and throw is at an IOException from Errno Exception
- return new IpSecTransformResponse(IpSecManager.Status.RESOURCE_UNAVAILABLE);
- }
+ createOrUpdateTransform(c, resourceId, spiRecord, socketRecord);
// SA was created successfully, time to construct a record and lock it away
userRecord.mTransformRecords.put(
@@ -1642,23 +1629,15 @@
c.getMode() == IpSecTransform.MODE_TRANSPORT,
"Transform mode was not Transport mode; cannot be applied to a socket");
- try {
- mSrvConfig
- .getNetdInstance()
- .ipSecApplyTransportModeTransform(
- socket.getFileDescriptor(),
- resourceId,
- direction,
- c.getSourceAddress(),
- c.getDestinationAddress(),
- info.getSpiRecord().getSpi());
- } catch (ServiceSpecificException e) {
- if (e.errorCode == EINVAL) {
- throw new IllegalArgumentException(e.toString());
- } else {
- throw e;
- }
- }
+ mSrvConfig
+ .getNetdInstance()
+ .ipSecApplyTransportModeTransform(
+ socket.getFileDescriptor(),
+ resourceId,
+ direction,
+ c.getSourceAddress(),
+ c.getDestinationAddress(),
+ info.getSpiRecord().getSpi());
}
/**
@@ -1670,13 +1649,9 @@
@Override
public synchronized void removeTransportModeTransforms(ParcelFileDescriptor socket)
throws RemoteException {
- try {
- mSrvConfig
- .getNetdInstance()
- .ipSecRemoveTransportModeTransform(socket.getFileDescriptor());
- } catch (ServiceSpecificException e) {
- // FIXME: get the error code and throw is at an IOException from Errno Exception
- }
+ mSrvConfig
+ .getNetdInstance()
+ .ipSecRemoveTransportModeTransform(socket.getFileDescriptor());
}
/**
diff --git a/services/core/java/com/android/server/TelephonyRegistry.java b/services/core/java/com/android/server/TelephonyRegistry.java
index 83fe976..41f413d 100644
--- a/services/core/java/com/android/server/TelephonyRegistry.java
+++ b/services/core/java/com/android/server/TelephonyRegistry.java
@@ -351,7 +351,7 @@
mCallForwarding[i] = false;
mCellLocation[i] = new Bundle();
mCellInfo.add(i, null);
- mPhysicalChannelConfigs.add(i, null);
+ mPhysicalChannelConfigs.add(i, new ArrayList<PhysicalChannelConfig>());
}
// Note that location can be null for non-phone builds like
@@ -1426,6 +1426,31 @@
}
}
+ public void notifyOemHookRawEventForSubscriber(int subId, byte[] rawData) {
+ if (!checkNotifyPermission("notifyOemHookRawEventForSubscriber")) {
+ return;
+ }
+
+ synchronized (mRecords) {
+ for (Record r : mRecords) {
+ if (VDBG) {
+ log("notifyOemHookRawEventForSubscriber: r=" + r + " subId=" + subId);
+ }
+ if ((r.matchPhoneStateListenerEvent(
+ PhoneStateListener.LISTEN_OEM_HOOK_RAW_EVENT)) &&
+ ((r.subId == subId) ||
+ (r.subId == SubscriptionManager.DEFAULT_SUBSCRIPTION_ID))) {
+ try {
+ r.callback.onOemHookRawEvent(rawData);
+ } catch (RemoteException ex) {
+ mRemoveList.add(r.binder);
+ }
+ }
+ }
+ handleRemoveListLocked();
+ }
+ }
+
@Override
public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " ");
@@ -1693,6 +1718,11 @@
android.Manifest.permission.READ_PRECISE_PHONE_STATE, null);
}
+ if ((events & PhoneStateListener.LISTEN_OEM_HOOK_RAW_EVENT) != 0) {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE, null);
+ }
+
return true;
}
diff --git a/services/core/java/com/android/server/VibratorService.java b/services/core/java/com/android/server/VibratorService.java
index 83d2bf7..ede870f 100644
--- a/services/core/java/com/android/server/VibratorService.java
+++ b/services/core/java/com/android/server/VibratorService.java
@@ -704,9 +704,11 @@
VibrationEffect scaledEffect = null;
if (vib.effect instanceof VibrationEffect.OneShot) {
VibrationEffect.OneShot oneShot = (VibrationEffect.OneShot) vib.effect;
+ oneShot = oneShot.resolve(mDefaultVibrationAmplitude);
scaledEffect = oneShot.scale(gamma, maxAmplitude);
} else if (vib.effect instanceof VibrationEffect.Waveform) {
VibrationEffect.Waveform waveform = (VibrationEffect.Waveform) vib.effect;
+ waveform = waveform.resolve(mDefaultVibrationAmplitude);
scaledEffect = waveform.scale(gamma, maxAmplitude);
} else {
Slog.w(TAG, "Unable to apply intensity scaling, unknown VibrationEffect type");
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 228171f..f413639 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -422,13 +422,9 @@
}
// If we're starting indirectly (e.g. from PendingIntent), figure out whether
- // we're launching into an app in a background state.
- final int uidState = mAm.getUidStateLocked(r.appInfo.uid);
- if (DEBUG_SERVICE) {
- Slog.v(TAG_SERVICE, "Uid state " + uidState + " indirect starting " + r.shortName);
- }
- final boolean bgLaunch = (uidState >
- ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND);
+ // we're launching into an app in a background state. This keys off of the same
+ // idleness state tracking as e.g. O+ background service start policy.
+ final boolean bgLaunch = !mAm.isUidActiveLocked(r.appInfo.uid);
// If the app has strict background restrictions, we treat any bg service
// start analogously to the legacy-app forced-restrictions case, regardless
@@ -1197,10 +1193,13 @@
if (!ignoreForeground &&
appRestrictedAnyInBackground(r.appInfo.uid, r.packageName)) {
- ignoreForeground = true;
Slog.w(TAG,
"Service.startForeground() not allowed due to bg restriction: service "
+ r.shortName);
+ // Back off of any foreground expectations around this service, since we've
+ // just turned down its fg request.
+ updateServiceForegroundLocked(r.app, false);
+ ignoreForeground = true;
}
// Apps under strict background restrictions simply don't get to have foreground
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index ae26c23..ac06ddd 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -22929,6 +22929,7 @@
private void noteUidProcessState(final int uid, final int state) {
mBatteryStatsService.noteUidProcessState(uid, state);
+ mAppOpsService.updateUidProcState(uid, state);
if (mTrackingAssociations) {
for (int i1=0, N1=mAssociations.size(); i1<N1; i1++) {
ArrayMap<ComponentName, SparseArray<ArrayMap<String, Association>>> targetComponents
@@ -26625,8 +26626,7 @@
@Override
public boolean isUidActive(int uid) {
synchronized (ActivityManagerService.this) {
- final UidRecord uidRec = mActiveUids.get(uid);
- return (uidRec != null) && !uidRec.idle;
+ return isUidActiveLocked(uid);
}
}
diff --git a/services/core/java/com/android/server/am/AppWarnings.java b/services/core/java/com/android/server/am/AppWarnings.java
index ea0251e..30a3844 100644
--- a/services/core/java/com/android/server/am/AppWarnings.java
+++ b/services/core/java/com/android/server/am/AppWarnings.java
@@ -122,7 +122,7 @@
return;
}
- // TODO(b/77862563): temp. fix while P is being finalized. To be reverted
+ // TODO(b/75318890): Need to move this to when the app actually crashes.
if (/*ActivityManager.isRunningInTestHarness()
&&*/ !mAlwaysShowUnsupportedCompileSdkWarningActivities.contains(r.realActivity)) {
// Don't show warning if we are running in a test harness and we don't have to always
diff --git a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
index 87194bc..49fa902 100644
--- a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
+++ b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
@@ -103,6 +103,9 @@
@GuardedBy("this")
private Future<?> mWakelockChangesUpdate;
+ @GuardedBy("this")
+ private Future<?> mBatteryLevelSync;
+
private final Object mWorkerLock = new Object();
@GuardedBy("mWorkerLock")
@@ -197,35 +200,75 @@
@Override
public Future<?> scheduleCpuSyncDueToWakelockChange(long delayMillis) {
- if (mExecutorService.isShutdown()) {
- return CompletableFuture.failedFuture(new IllegalStateException("worker shutdown"));
+ synchronized (BatteryExternalStatsWorker.this) {
+ mWakelockChangesUpdate = scheduleDelayedSyncLocked(mWakelockChangesUpdate,
+ () -> {
+ scheduleSync("wakelock-change", UPDATE_CPU);
+ scheduleRunnable(() -> mStats.postBatteryNeedsCpuUpdateMsg());
+ },
+ delayMillis);
+ return mWakelockChangesUpdate;
}
-
- if (mWakelockChangesUpdate != null) {
- // If there's already a scheduled task, leave it as is if we're trying to re-schedule
- // it again with a delay, otherwise cancel and re-schedule it.
- if (delayMillis == 0) {
- mWakelockChangesUpdate.cancel(false);
- } else {
- return mWakelockChangesUpdate;
- }
- }
-
- mWakelockChangesUpdate = mExecutorService.schedule(() -> {
- scheduleSync("wakelock-change", UPDATE_CPU);
- scheduleRunnable(() -> mStats.postBatteryNeedsCpuUpdateMsg());
- mWakelockChangesUpdate = null;
- }, delayMillis, TimeUnit.MILLISECONDS);
- return mWakelockChangesUpdate;
}
@Override
public void cancelCpuSyncDueToWakelockChange() {
- if (mWakelockChangesUpdate != null) {
- mWakelockChangesUpdate.cancel(false);
+ synchronized (BatteryExternalStatsWorker.this) {
+ if (mWakelockChangesUpdate != null) {
+ mWakelockChangesUpdate.cancel(false);
+ mWakelockChangesUpdate = null;
+ }
}
}
+ @Override
+ public Future<?> scheduleSyncDueToBatteryLevelChange(long delayMillis) {
+ synchronized (BatteryExternalStatsWorker.this) {
+ mBatteryLevelSync = scheduleDelayedSyncLocked(mBatteryLevelSync,
+ () -> scheduleSync("battery-level", UPDATE_ALL),
+ delayMillis);
+ return mBatteryLevelSync;
+ }
+ }
+
+ @GuardedBy("this")
+ private void cancelSyncDueToBatteryLevelChangeLocked() {
+ if (mBatteryLevelSync != null) {
+ mBatteryLevelSync.cancel(false);
+ mBatteryLevelSync = null;
+ }
+ }
+
+ /**
+ * Schedule a sync {@param syncRunnable} with a delay. If there's already a scheduled sync, a
+ * new sync won't be scheduled unless it is being scheduled to run immediately (delayMillis=0).
+ *
+ * @param lastScheduledSync the task which was earlier scheduled to run
+ * @param syncRunnable the task that needs to be scheduled to run
+ * @param delayMillis time after which {@param syncRunnable} needs to be scheduled
+ * @return scheduled {@link Future} which can be used to check if task is completed or to
+ * cancel it if needed
+ */
+ @GuardedBy("this")
+ private Future<?> scheduleDelayedSyncLocked(Future<?> lastScheduledSync, Runnable syncRunnable,
+ long delayMillis) {
+ if (mExecutorService.isShutdown()) {
+ return CompletableFuture.failedFuture(new IllegalStateException("worker shutdown"));
+ }
+
+ if (lastScheduledSync != null) {
+ // If there's already a scheduled task, leave it as is if we're trying to
+ // re-schedule it again with a delay, otherwise cancel and re-schedule it.
+ if (delayMillis == 0) {
+ lastScheduledSync.cancel(false);
+ } else {
+ return lastScheduledSync;
+ }
+ }
+
+ return mExecutorService.schedule(syncRunnable, delayMillis, TimeUnit.MILLISECONDS);
+ }
+
public synchronized Future<?> scheduleWrite() {
if (mExecutorService.isShutdown()) {
return CompletableFuture.failedFuture(new IllegalStateException("worker shutdown"));
@@ -294,6 +337,12 @@
mUidsToRemove.clear();
mCurrentFuture = null;
mUseLatestStates = true;
+ if ((updateFlags & UPDATE_ALL) != 0) {
+ cancelSyncDueToBatteryLevelChangeLocked();
+ }
+ if ((updateFlags & UPDATE_CPU) != 0) {
+ cancelCpuSyncDueToWakelockChange();
+ }
}
try {
diff --git a/services/core/java/com/android/server/am/CarUserSwitchingDialog.java b/services/core/java/com/android/server/am/CarUserSwitchingDialog.java
index 676f0c7..1149e87 100644
--- a/services/core/java/com/android/server/am/CarUserSwitchingDialog.java
+++ b/services/core/java/com/android/server/am/CarUserSwitchingDialog.java
@@ -23,6 +23,7 @@
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
+import android.os.UserManager;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ImageView;
@@ -59,11 +60,14 @@
View view = LayoutInflater.from(getContext()).inflate(R.layout.car_user_switching_dialog,
null);
- FileDescriptor fileDescriptor = UserManagerService.getInstance()
- .getUserIcon(mNewUser.id).getFileDescriptor();
- Bitmap bitmap = BitmapFactory.decodeFileDescriptor(fileDescriptor);
- ((ImageView) view.findViewById(R.id.user_loading_avatar))
- .setImageBitmap(bitmap);
+ UserManager userManager =
+ (UserManager) getContext().getSystemService(Context.USER_SERVICE);
+ Bitmap bitmap = userManager.getUserIcon(mNewUser.id);
+ if (bitmap != null) {
+ ((ImageView) view.findViewById(R.id.user_loading_avatar))
+ .setImageBitmap(bitmap);
+ }
+
((TextView) view.findViewById(R.id.user_loading))
.setText(res.getString(R.string.car_loading_profile));
setView(view);
diff --git a/services/core/java/com/android/server/am/KeyguardController.java b/services/core/java/com/android/server/am/KeyguardController.java
index 1b7f75b..5764382 100644
--- a/services/core/java/com/android/server/am/KeyguardController.java
+++ b/services/core/java/com/android/server/am/KeyguardController.java
@@ -121,6 +121,8 @@
void setKeyguardShown(boolean keyguardShowing, boolean aodShowing,
int secondaryDisplayShowing) {
boolean showingChanged = keyguardShowing != mKeyguardShowing || aodShowing != mAodShowing;
+ // If keyguard is going away, but SystemUI aborted the transition, need to reset state.
+ showingChanged |= mKeyguardGoingAway && keyguardShowing;
if (!showingChanged && secondaryDisplayShowing == mSecondaryDisplayShowing) {
return;
}
diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java
index 550c37a..483fec6 100644
--- a/services/core/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/core/java/com/android/server/am/PendingIntentRecord.java
@@ -219,6 +219,9 @@
}
public void unregisterCancelListenerLocked(IResultReceiver receiver) {
+ if (mCancelCallbacks == null) {
+ return; // Already unregistered or detached.
+ }
mCancelCallbacks.unregister(receiver);
if (mCancelCallbacks.getRegisteredCallbackCount() <= 0) {
mCancelCallbacks = null;
diff --git a/services/core/java/com/android/server/am/RecentTasks.java b/services/core/java/com/android/server/am/RecentTasks.java
index c83bacb..06a3078 100644
--- a/services/core/java/com/android/server/am/RecentTasks.java
+++ b/services/core/java/com/android/server/am/RecentTasks.java
@@ -22,8 +22,10 @@
import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
+import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
@@ -1255,7 +1257,8 @@
for (int i = 0; i < recentsCount; i++) {
final TaskRecord tr = mTasks.get(i);
if (task != tr) {
- if (!task.hasCompatibleActivityType(tr) || task.userId != tr.userId) {
+ if (!hasCompatibleActivityTypeAndWindowingMode(task, tr)
+ || task.userId != tr.userId) {
continue;
}
final Intent trIntent = tr.intent;
@@ -1547,4 +1550,29 @@
return rti;
}
+
+ /**
+ * @return Whether the activity types and windowing modes of the two tasks are considered
+ * compatible. This is necessary because we currently don't persist the activity type
+ * or the windowing mode with the task, so they can be undefined when restored.
+ */
+ private boolean hasCompatibleActivityTypeAndWindowingMode(TaskRecord t1, TaskRecord t2) {
+ final int activityType = t1.getActivityType();
+ final int windowingMode = t1.getWindowingMode();
+ final boolean isUndefinedType = activityType == ACTIVITY_TYPE_UNDEFINED;
+ final boolean isUndefinedMode = windowingMode == WINDOWING_MODE_UNDEFINED;
+ final int otherActivityType = t2.getActivityType();
+ final int otherWindowingMode = t2.getWindowingMode();
+ final boolean isOtherUndefinedType = otherActivityType == ACTIVITY_TYPE_UNDEFINED;
+ final boolean isOtherUndefinedMode = otherWindowingMode == WINDOWING_MODE_UNDEFINED;
+
+ // An activity type and windowing mode is compatible if they are the exact same type/mode,
+ // or if one of the type/modes is undefined
+ final boolean isCompatibleType = activityType == otherActivityType
+ || isUndefinedType || isOtherUndefinedType;
+ final boolean isCompatibleMode = windowingMode == otherWindowingMode
+ || isUndefinedMode || isOtherUndefinedMode;
+
+ return isCompatibleType && isCompatibleMode;
+ }
}
diff --git a/services/core/java/com/android/server/am/RecentsAnimation.java b/services/core/java/com/android/server/am/RecentsAnimation.java
index b14f5b6..b5047ae 100644
--- a/services/core/java/com/android/server/am/RecentsAnimation.java
+++ b/services/core/java/com/android/server/am/RecentsAnimation.java
@@ -101,9 +101,7 @@
: ACTIVITY_TYPE_HOME;
final ActivityStack targetStack = mDefaultDisplay.getStack(WINDOWING_MODE_UNDEFINED,
mTargetActivityType);
- ActivityRecord targetActivity = targetStack != null
- ? targetStack.getTopActivity()
- : null;
+ ActivityRecord targetActivity = getTargetActivity(targetStack, intent.getComponent());
final boolean hasExistingActivity = targetActivity != null;
if (hasExistingActivity) {
final ActivityDisplay display = targetActivity.getDisplay();
@@ -149,6 +147,14 @@
display.moveStackBehindBottomMostVisibleStack(targetStack);
if (DEBUG) Slog.d(TAG, "Moved stack=" + targetStack + " behind stack="
+ display.getStackAbove(targetStack));
+
+ // If there are multiple tasks in the target stack (ie. the home stack, with 3p
+ // and default launchers coexisting), then move the task to the top as a part of
+ // moving the stack to the front
+ if (targetStack.topTask() != targetActivity.getTask()) {
+ targetStack.addTask(targetActivity.getTask(), true /* toTop */,
+ "startRecentsActivity");
+ }
} else {
// No recents activity
ActivityOptions options = ActivityOptions.makeBasic();
@@ -332,4 +338,22 @@
}
return null;
}
+
+ /**
+ * @return the top activity in the {@param targetStack} matching the {@param component}, or just
+ * the top activity of the top task if no task matches the component.
+ */
+ private ActivityRecord getTargetActivity(ActivityStack targetStack, ComponentName component) {
+ if (targetStack == null) {
+ return null;
+ }
+
+ for (int i = targetStack.getChildCount() - 1; i >= 0; i--) {
+ final TaskRecord task = (TaskRecord) targetStack.getChildAt(i);
+ if (task.getBaseIntent().getComponent().equals(component)) {
+ return task.getTopActivity();
+ }
+ }
+ return targetStack.getTopActivity();
+ }
}
diff --git a/services/core/java/com/android/server/clipboard/ClipboardService.java b/services/core/java/com/android/server/clipboard/ClipboardService.java
index f74ac47..5db20b0 100644
--- a/services/core/java/com/android/server/clipboard/ClipboardService.java
+++ b/services/core/java/com/android/server/clipboard/ClipboardService.java
@@ -621,7 +621,7 @@
private boolean clipboardAccessAllowed(int op, String callingPackage, int callingUid) {
// Check the AppOp.
- if (mAppOps.checkOp(op, callingUid, callingPackage) != AppOpsManager.MODE_ALLOWED) {
+ if (mAppOps.noteOp(op, callingUid, callingPackage) != AppOpsManager.MODE_ALLOWED) {
return false;
}
try {
diff --git a/services/core/java/com/android/server/fingerprint/FingerprintService.java b/services/core/java/com/android/server/fingerprint/FingerprintService.java
index ef40a1c..c9f92d2 100644
--- a/services/core/java/com/android/server/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/fingerprint/FingerprintService.java
@@ -619,6 +619,15 @@
void startRemove(IBinder token, int fingerId, int groupId, int userId,
IFingerprintServiceReceiver receiver, boolean restricted, boolean internal) {
+ if (token == null) {
+ Slog.w(TAG, "startRemove: token is null");
+ return;
+ }
+ if (receiver == null) {
+ Slog.w(TAG, "startRemove: receiver is null");
+ return;
+ }
+
IBiometricsFingerprint daemon = getFingerprintDaemon();
if (daemon == null) {
Slog.w(TAG, "startRemove: no fingerprint HAL!");
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index 312b21c..64750b0 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -818,37 +818,7 @@
}
};
- mGnssMeasurementsProvider = new GnssMeasurementsProvider(mHandler) {
- @Override
- public boolean isAvailableInPlatform() {
- return native_is_measurement_supported();
- }
-
- @Override
- protected int registerWithService() {
- int devOptions = Settings.Secure.getInt(mContext.getContentResolver(),
- Settings.Global.DEVELOPMENT_SETTINGS_ENABLED , 0);
- int fullTrackingToggled = Settings.Global.getInt(mContext.getContentResolver(),
- Settings.Global.ENABLE_GNSS_RAW_MEAS_FULL_TRACKING , 0);
- boolean result = false;
- if (devOptions == 1 /* Developer Mode enabled */
- && fullTrackingToggled == 1 /* Raw Measurements Full Tracking enabled */) {
- result = native_start_measurement_collection(true /* enableFullTracking */);
- } else {
- result = native_start_measurement_collection(false /* enableFullTracking */);
- }
- if (result) {
- return RemoteListenerHelper.RESULT_SUCCESS;
- } else {
- return RemoteListenerHelper.RESULT_INTERNAL_ERROR;
- }
- }
-
- @Override
- protected void unregisterFromService() {
- native_stop_measurement_collection();
- }
-
+ mGnssMeasurementsProvider = new GnssMeasurementsProvider(mContext, mHandler) {
@Override
protected boolean isGpsEnabled() {
return isEnabled();
@@ -857,26 +827,6 @@
mGnssNavigationMessageProvider = new GnssNavigationMessageProvider(mHandler) {
@Override
- protected boolean isAvailableInPlatform() {
- return native_is_navigation_message_supported();
- }
-
- @Override
- protected int registerWithService() {
- boolean result = native_start_navigation_message_collection();
- if (result) {
- return RemoteListenerHelper.RESULT_SUCCESS;
- } else {
- return RemoteListenerHelper.RESULT_INTERNAL_ERROR;
- }
- }
-
- @Override
- protected void unregisterFromService() {
- native_stop_navigation_message_collection();
- }
-
- @Override
protected boolean isGpsEnabled() {
return isEnabled();
}
@@ -1032,7 +982,7 @@
Log.e(TAG, "Invalid status to release SUPL connection: " + agpsDataConnStatus);
}
}
-
+
private void handleRequestLocation(boolean independentFromGnss) {
if (isRequestLocationRateLimited()) {
if (DEBUG) {
@@ -2790,20 +2740,6 @@
private native void native_update_network_state(boolean connected, int type,
boolean roaming, boolean available, String extraInfo, String defaultAPN);
- // Gps Hal measurements support.
- private static native boolean native_is_measurement_supported();
-
- private native boolean native_start_measurement_collection(boolean enableFullTracking);
-
- private native boolean native_stop_measurement_collection();
-
- // Gps Navigation message support.
- private static native boolean native_is_navigation_message_supported();
-
- private native boolean native_start_navigation_message_collection();
-
- private native boolean native_stop_navigation_message_collection();
-
// GNSS Configuration
private static native boolean native_set_supl_version(int version);
diff --git a/services/core/java/com/android/server/location/GnssMeasurementsProvider.java b/services/core/java/com/android/server/location/GnssMeasurementsProvider.java
index 477dae6..0add863 100644
--- a/services/core/java/com/android/server/location/GnssMeasurementsProvider.java
+++ b/services/core/java/com/android/server/location/GnssMeasurementsProvider.java
@@ -16,12 +16,16 @@
package com.android.server.location;
+import android.content.Context;
import android.location.GnssMeasurementsEvent;
import android.location.IGnssMeasurementsListener;
import android.os.Handler;
import android.os.RemoteException;
+import android.provider.Settings;
import android.util.Log;
+import com.android.internal.annotations.VisibleForTesting;
+
/**
* An base implementation for GPS measurements provider.
* It abstracts out the responsibility of handling listeners, while still allowing technology
@@ -29,22 +33,73 @@
*
* @hide
*/
-public abstract class GnssMeasurementsProvider
- extends RemoteListenerHelper<IGnssMeasurementsListener> {
+public abstract class GnssMeasurementsProvider extends
+ RemoteListenerHelper<IGnssMeasurementsListener> {
private static final String TAG = "GnssMeasurementsProvider";
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
- protected GnssMeasurementsProvider(Handler handler) {
+ private final Context mContext;
+ private final GnssMeasurementProviderNative mNative;
+
+ private boolean mIsCollectionStarted;
+ private boolean mEnableFullTracking;
+
+ protected GnssMeasurementsProvider(Context context, Handler handler) {
+ this(context, handler, new GnssMeasurementProviderNative());
+ }
+
+ @VisibleForTesting
+ GnssMeasurementsProvider(Context context, Handler handler,
+ GnssMeasurementProviderNative aNative) {
super(handler, TAG);
+ mContext = context;
+ mNative = aNative;
+ }
+
+ // TODO(b/37460011): Use this with death recovery logic.
+ void resumeIfStarted() {
+ if (DEBUG) {
+ Log.d(TAG, "resumeIfStarted");
+ }
+ if (mIsCollectionStarted) {
+ mNative.startMeasurementCollection(mEnableFullTracking);
+ }
+ }
+
+ @Override
+ public boolean isAvailableInPlatform() {
+ return mNative.isMeasurementSupported();
+ }
+
+ @Override
+ protected int registerWithService() {
+ int devOptions = Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0);
+ int fullTrackingToggled = Settings.Global.getInt(mContext.getContentResolver(),
+ Settings.Global.ENABLE_GNSS_RAW_MEAS_FULL_TRACKING, 0);
+ boolean enableFullTracking = (devOptions == 1 /* Developer Mode enabled */)
+ && (fullTrackingToggled == 1 /* Raw Measurements Full Tracking enabled */);
+ boolean result = mNative.startMeasurementCollection(enableFullTracking);
+ if (result) {
+ mIsCollectionStarted = true;
+ mEnableFullTracking = enableFullTracking;
+ return RemoteListenerHelper.RESULT_SUCCESS;
+ } else {
+ return RemoteListenerHelper.RESULT_INTERNAL_ERROR;
+ }
+ }
+
+ @Override
+ protected void unregisterFromService() {
+ boolean stopped = mNative.stopMeasurementCollection();
+ if (stopped) {
+ mIsCollectionStarted = false;
+ }
}
public void onMeasurementsAvailable(final GnssMeasurementsEvent event) {
ListenerOperation<IGnssMeasurementsListener> operation =
- new ListenerOperation<IGnssMeasurementsListener>() {
- @Override
- public void execute(IGnssMeasurementsListener listener) throws RemoteException {
- listener.onGnssMeasurementsReceived(event);
- }
- };
+ listener -> listener.onGnssMeasurementsReceived(event);
foreach(operation);
}
@@ -98,4 +153,25 @@
listener.onStatusChanged(mStatus);
}
}
+
+ @VisibleForTesting
+ static class GnssMeasurementProviderNative {
+ public boolean isMeasurementSupported() {
+ return native_is_measurement_supported();
+ }
+
+ public boolean startMeasurementCollection(boolean enableFullTracking) {
+ return native_start_measurement_collection(enableFullTracking);
+ }
+
+ public boolean stopMeasurementCollection() {
+ return native_stop_measurement_collection();
+ }
+ }
+
+ private static native boolean native_is_measurement_supported();
+
+ private static native boolean native_start_measurement_collection(boolean enableFullTracking);
+
+ private static native boolean native_stop_measurement_collection();
}
diff --git a/services/core/java/com/android/server/location/GnssNavigationMessageProvider.java b/services/core/java/com/android/server/location/GnssNavigationMessageProvider.java
index df3c49b..1b4fd18 100644
--- a/services/core/java/com/android/server/location/GnssNavigationMessageProvider.java
+++ b/services/core/java/com/android/server/location/GnssNavigationMessageProvider.java
@@ -22,6 +22,8 @@
import android.os.RemoteException;
import android.util.Log;
+import com.android.internal.annotations.VisibleForTesting;
+
/**
* An base implementation for GPS navigation messages provider.
* It abstracts out the responsibility of handling listeners, while still allowing technology
@@ -32,9 +34,53 @@
public abstract class GnssNavigationMessageProvider
extends RemoteListenerHelper<IGnssNavigationMessageListener> {
private static final String TAG = "GnssNavigationMessageProvider";
+ private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+ private final GnssNavigationMessageProviderNative mNative;
+ private boolean mCollectionStarted;
protected GnssNavigationMessageProvider(Handler handler) {
+ this(handler, new GnssNavigationMessageProviderNative());
+ }
+
+ @VisibleForTesting
+ GnssNavigationMessageProvider(Handler handler, GnssNavigationMessageProviderNative aNative) {
super(handler, TAG);
+ mNative = aNative;
+ }
+
+ // TODO(b/37460011): Use this with death recovery logic.
+ void resumeIfStarted() {
+ if (DEBUG) {
+ Log.d(TAG, "resumeIfStarted");
+ }
+ if (mCollectionStarted) {
+ mNative.startNavigationMessageCollection();
+ }
+ }
+
+ @Override
+ protected boolean isAvailableInPlatform() {
+ return mNative.isNavigationMessageSupported();
+ }
+
+ @Override
+ protected int registerWithService() {
+ boolean result = mNative.startNavigationMessageCollection();
+ if (result) {
+ mCollectionStarted = true;
+ return RemoteListenerHelper.RESULT_SUCCESS;
+ } else {
+ return RemoteListenerHelper.RESULT_INTERNAL_ERROR;
+ }
+ }
+
+ @Override
+ protected void unregisterFromService() {
+ boolean stopped = mNative.stopNavigationMessageCollection();
+ if (stopped) {
+ mCollectionStarted = false;
+ }
}
public void onNavigationMessageAvailable(final GnssNavigationMessage event) {
@@ -96,4 +142,25 @@
listener.onStatusChanged(mStatus);
}
}
+
+ @VisibleForTesting
+ static class GnssNavigationMessageProviderNative {
+ public boolean isNavigationMessageSupported() {
+ return native_is_navigation_message_supported();
+ }
+
+ public boolean startNavigationMessageCollection() {
+ return native_start_navigation_message_collection();
+ }
+
+ public boolean stopNavigationMessageCollection() {
+ return native_stop_navigation_message_collection();
+ }
+ }
+
+ private static native boolean native_is_navigation_message_supported();
+
+ private static native boolean native_start_navigation_message_collection();
+
+ private static native boolean native_stop_navigation_message_collection();
}
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index 269a0da..f7becd5 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -72,6 +72,8 @@
import java.util.HashSet;
import java.util.List;
import java.util.Set;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
/**
* Manages the lifecycle of application-provided services bound by system server.
@@ -336,7 +338,7 @@
loadAllowedComponentsFromSettings();
}
- public void readXml(XmlPullParser parser)
+ public void readXml(XmlPullParser parser, Predicate<String> allowedManagedServicePackages)
throws XmlPullParserException, IOException {
// upgrade xml
int xmlVersion = XmlUtils.readIntAttribute(parser, ATT_VERSION, 0);
@@ -361,10 +363,14 @@
final int userId = XmlUtils.readIntAttribute(parser, ATT_USER_ID, 0);
final boolean isPrimary =
XmlUtils.readBooleanAttribute(parser, ATT_IS_PRIMARY, true);
- if (mUm.getUserInfo(userId) != null) {
- addApprovedList(approved, userId, isPrimary);
+
+ if (allowedManagedServicePackages == null ||
+ allowedManagedServicePackages.test(getPackageName(approved))) {
+ if (mUm.getUserInfo(userId) != null) {
+ addApprovedList(approved, userId, isPrimary);
+ }
+ mUseXml = true;
}
- mUseXml = true;
}
}
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 5948864..ec3949f 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -233,6 +233,7 @@
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.TimeUnit;
+import java.util.function.Predicate;
/** {@hide} */
public class NotificationManagerService extends SystemService {
@@ -397,6 +398,7 @@
private boolean mIsTelevision;
private MetricsLogger mMetricsLogger;
+ private Predicate<String> mAllowedManagedServicePackages;
private static class Archive {
final int mBufferSize;
@@ -518,18 +520,15 @@
} else if (RankingHelper.TAG_RANKING.equals(parser.getName())){
mRankingHelper.readXml(parser, forRestore);
}
- // No non-system managed services are allowed on low ram devices
- if (canUseManagedServices()) {
- if (mListeners.getConfig().xmlTag.equals(parser.getName())) {
- mListeners.readXml(parser);
- migratedManagedServices = true;
- } else if (mAssistants.getConfig().xmlTag.equals(parser.getName())) {
- mAssistants.readXml(parser);
- migratedManagedServices = true;
- } else if (mConditionProviders.getConfig().xmlTag.equals(parser.getName())) {
- mConditionProviders.readXml(parser);
- migratedManagedServices = true;
- }
+ if (mListeners.getConfig().xmlTag.equals(parser.getName())) {
+ mListeners.readXml(parser, mAllowedManagedServicePackages);
+ migratedManagedServices = true;
+ } else if (mAssistants.getConfig().xmlTag.equals(parser.getName())) {
+ mAssistants.readXml(parser, mAllowedManagedServicePackages);
+ migratedManagedServices = true;
+ } else if (mConditionProviders.getConfig().xmlTag.equals(parser.getName())) {
+ mConditionProviders.readXml(parser, mAllowedManagedServicePackages);
+ migratedManagedServices = true;
}
}
@@ -1429,6 +1428,9 @@
// This is a MangedServices object that keeps track of the assistant.
mAssistants = notificationAssistants;
+ // Needs to be set before loadPolicyFile
+ mAllowedManagedServicePackages = this::canUseManagedServices;
+
mPolicyFile = policyFile;
loadPolicyFile();
@@ -3218,7 +3220,7 @@
checkCallerIsSystemOrShell();
final long identity = Binder.clearCallingIdentity();
try {
- if (canUseManagedServices()) {
+ if (mAllowedManagedServicePackages.test(pkg)) {
mConditionProviders.setPackageOrComponentEnabled(
pkg, userId, true, granted);
@@ -3349,7 +3351,7 @@
checkCallerIsSystemOrShell();
final long identity = Binder.clearCallingIdentity();
try {
- if (canUseManagedServices()) {
+ if (mAllowedManagedServicePackages.test(listener.getPackageName())) {
mConditionProviders.setPackageOrComponentEnabled(listener.flattenToString(),
userId, false, granted);
mListeners.setPackageOrComponentEnabled(listener.flattenToString(),
@@ -3375,7 +3377,7 @@
checkCallerIsSystemOrShell();
final long identity = Binder.clearCallingIdentity();
try {
- if (canUseManagedServices()) {
+ if (mAllowedManagedServicePackages.test(assistant.getPackageName())) {
mConditionProviders.setPackageOrComponentEnabled(assistant.flattenToString(),
userId, false, granted);
mAssistants.setPackageOrComponentEnabled(assistant.flattenToString(),
@@ -6183,9 +6185,19 @@
}
}
- private boolean canUseManagedServices() {
- return !mActivityManager.isLowRamDevice()
+ @VisibleForTesting
+ boolean canUseManagedServices(String pkg) {
+ boolean canUseManagedServices = !mActivityManager.isLowRamDevice()
|| mPackageManagerClient.hasSystemFeature(PackageManager.FEATURE_WATCH);
+
+ for (String whitelisted : getContext().getResources().getStringArray(
+ R.array.config_allowedManagedServicesOnLowRamDevices)) {
+ if (whitelisted.equals(pkg)) {
+ canUseManagedServices = true;
+ }
+ }
+
+ return canUseManagedServices;
}
private class TrimCache {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 50eaa5c..bb1f5c0 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -4223,6 +4223,11 @@
|| appId == Process.ROOT_UID) {
return false;
}
+ // Installer gets to see all static libs.
+ if (PackageManager.PERMISSION_GRANTED
+ == checkUidPermission(Manifest.permission.INSTALL_PACKAGES, uid)) {
+ return false;
+ }
}
// No package means no static lib as it is always on internal storage
diff --git a/services/core/java/com/android/server/slice/SliceManagerService.java b/services/core/java/com/android/server/slice/SliceManagerService.java
index a9cdafd..507f0a8 100644
--- a/services/core/java/com/android/server/slice/SliceManagerService.java
+++ b/services/core/java/com/android/server/slice/SliceManagerService.java
@@ -155,11 +155,10 @@
enforceAccess(pkg, uri);
int user = Binder.getCallingUserHandle().getIdentifier();
uri = maybeAddUserId(uri, user);
- getOrCreatePinnedSlice(uri, pkg).pin(pkg, specs, token);
+ String slicePkg = getProviderPkg(uri, user);
+ getOrCreatePinnedSlice(uri, slicePkg).pin(pkg, specs, token);
- Uri finalUri = uri;
mHandler.post(() -> {
- String slicePkg = getProviderPkg(finalUri, user);
if (slicePkg != null && !Objects.equals(pkg, slicePkg)) {
mAppUsageStats.reportEvent(slicePkg, user,
isAssistant(pkg, user) || isDefaultHomeApp(pkg, user)
diff --git a/services/core/java/com/android/server/wm/BoundsAnimationController.java b/services/core/java/com/android/server/wm/BoundsAnimationController.java
index 112d93c..b2a12be 100644
--- a/services/core/java/com/android/server/wm/BoundsAnimationController.java
+++ b/services/core/java/com/android/server/wm/BoundsAnimationController.java
@@ -401,12 +401,13 @@
+ " replacing=" + replacing);
if (replacing) {
- if (existing.isAnimatingTo(to)) {
+ if (existing.isAnimatingTo(to) && (!moveToFullscreen || existing.mMoveToFullscreen)
+ && (!moveFromFullscreen || existing.mMoveFromFullscreen)) {
// Just let the current animation complete if it has the same destination as the
- // one we are trying to start.
- if (DEBUG) Slog.d(TAG, "animateBounds: same destination as existing=" + existing
- + " ignoring...");
-
+ // one we are trying to start, and, if moveTo/FromFullscreen was requested, already
+ // has that flag set.
+ if (DEBUG) Slog.d(TAG, "animateBounds: same destination and moveTo/From flags as "
+ + "existing=" + existing + ", ignoring...");
return existing;
}
@@ -434,6 +435,13 @@
}
}
+ // We need to keep the previous moveTo/FromFullscreen flag, unless the new animation
+ // specifies a direction.
+ if (!moveFromFullscreen && !moveToFullscreen) {
+ moveToFullscreen = existing.mMoveToFullscreen;
+ moveFromFullscreen = existing.mMoveFromFullscreen;
+ }
+
// Since we are replacing, we skip both animation start and end callbacks
existing.cancel();
}
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index eab391e..19c5a3d 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -922,6 +922,10 @@
* @return Whether this WindowContainer should be magnified by the accessibility magnifier.
*/
boolean shouldMagnify() {
+ if (mSurfaceControl == null) {
+ return false;
+ }
+
for (int i = 0; i < mChildren.size(); i++) {
if (!mChildren.get(i).shouldMagnify()) {
return false;
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 6c2821d..86aed47 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -3865,9 +3865,13 @@
windowInfo.title = mAttrs.accessibilityTitle;
// Panel windows have no public way to set the a11y title directly. Use the
// regular title as a fallback.
- if (TextUtils.isEmpty(windowInfo.title)
- && (mAttrs.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW)
- && (mAttrs.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW)) {
+ final boolean isPanelWindow = (mAttrs.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW)
+ && (mAttrs.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW);
+ // Accessibility overlays should have titles that work for accessibility, and can't set
+ // the a11y title themselves.
+ final boolean isAccessibilityOverlay =
+ windowInfo.type == WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
+ if (TextUtils.isEmpty(windowInfo.title) && (isPanelWindow || isAccessibilityOverlay)) {
windowInfo.title = mAttrs.getTitle();
}
windowInfo.accessibilityIdOfAnchor = mAttrs.accessibilityIdOfAnchor;
@@ -4270,6 +4274,24 @@
}
}
+ private boolean skipDecorCrop() {
+ // The decor frame is used to specify the region not covered by the system
+ // decorations (nav bar, status bar). In case this is empty, for example with
+ // FLAG_TRANSLUCENT_NAVIGATION, we don't need to do any cropping.
+ if (mDecorFrame.isEmpty()) {
+ return true;
+ }
+
+ // But if we have a frame, and are an application window, then we must be cropped.
+ if (mAppToken != null) {
+ return false;
+ }
+
+ // For non application windows, we may be allowed to extend over the decor bars
+ // depending on our type and permissions assosciated with our token.
+ return mToken.canLayerAboveSystemBars();
+ }
+
/**
* Calculate the window crop according to system decor policy. In general this is
* the system decor rect (see #calculateSystemDecorRect), but we also have some
@@ -4287,7 +4309,7 @@
policyCrop.intersect(-mCompatFrame.left, -mCompatFrame.top,
displayInfo.logicalWidth - mCompatFrame.left,
displayInfo.logicalHeight - mCompatFrame.top);
- } else if (mDecorFrame.isEmpty()) {
+ } else if (skipDecorCrop()) {
// Windows without policy decor aren't cropped.
policyCrop.set(0, 0, mCompatFrame.width(), mCompatFrame.height());
} else {
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index 14680d9..b97460a 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -17,7 +17,7 @@
package com.android.server.wm;
import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
-
+import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WINDOW_MOVEMENT;
@@ -336,4 +336,16 @@
boolean okToAnimate() {
return mDisplayContent != null && mDisplayContent.okToAnimate();
}
+
+ /**
+ * Return whether windows from this token can layer above the
+ * system bars, or in other words extend outside of the "Decor Frame"
+ */
+ boolean canLayerAboveSystemBars() {
+ int layer = mService.mPolicy.getWindowLayerFromTypeLw(windowType,
+ mOwnerCanManageAppTokens);
+ int navLayer = mService.mPolicy.getWindowLayerFromTypeLw(TYPE_NAVIGATION_BAR,
+ mOwnerCanManageAppTokens);
+ return mOwnerCanManageAppTokens && (layer > navLayer);
+ }
}
diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index 3a9bbe4..a3a7e1e 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -1799,7 +1799,7 @@
return JNI_FALSE;
}
-static jboolean android_location_GnssLocationProvider_is_measurement_supported(
+static jboolean android_location_GnssMeasurementsProvider_is_measurement_supported(
JNIEnv* env, jclass clazz) {
if (gnssMeasurementIface != nullptr) {
return JNI_TRUE;
@@ -1808,7 +1808,7 @@
return JNI_FALSE;
}
-static jboolean android_location_GnssLocationProvider_start_measurement_collection(
+static jboolean android_location_GnssMeasurementsProvider_start_measurement_collection(
JNIEnv* /* env */,
jobject /* obj */,
jboolean enableFullTracking) {
@@ -1842,7 +1842,7 @@
return JNI_TRUE;
}
-static jboolean android_location_GnssLocationProvider_stop_measurement_collection(
+static jboolean android_location_GnssMeasurementsProvider_stop_measurement_collection(
JNIEnv* env,
jobject obj) {
if (gnssMeasurementIface == nullptr) {
@@ -1854,7 +1854,7 @@
return boolToJbool(result.isOk());
}
-static jboolean android_location_GnssLocationProvider_is_navigation_message_supported(
+static jboolean android_location_GnssNavigationMessageProvider_is_navigation_message_supported(
JNIEnv* env,
jclass clazz) {
if (gnssNavigationMessageIface != nullptr) {
@@ -1863,7 +1863,7 @@
return JNI_FALSE;
}
-static jboolean android_location_GnssLocationProvider_start_navigation_message_collection(
+static jboolean android_location_GnssNavigationMessageProvider_start_navigation_message_collection(
JNIEnv* env,
jobject obj) {
if (gnssNavigationMessageIface == nullptr) {
@@ -1884,7 +1884,7 @@
return JNI_TRUE;
}
-static jboolean android_location_GnssLocationProvider_stop_navigation_message_collection(
+static jboolean android_location_GnssNavigationMessageProvider_stop_navigation_message_collection(
JNIEnv* env,
jobject obj) {
if (gnssNavigationMessageIface == nullptr) {
@@ -2178,30 +2178,6 @@
{"native_update_network_state",
"(ZIZZLjava/lang/String;Ljava/lang/String;)V",
reinterpret_cast<void *>(android_location_GnssLocationProvider_update_network_state)},
- {"native_is_measurement_supported",
- "()Z",
- reinterpret_cast<void *>(
- android_location_GnssLocationProvider_is_measurement_supported)},
- {"native_start_measurement_collection",
- "(Z)Z",
- reinterpret_cast<void *>(
- android_location_GnssLocationProvider_start_measurement_collection)},
- {"native_stop_measurement_collection",
- "()Z",
- reinterpret_cast<void *>(
- android_location_GnssLocationProvider_stop_measurement_collection)},
- {"native_is_navigation_message_supported",
- "()Z",
- reinterpret_cast<void *>(
- android_location_GnssLocationProvider_is_navigation_message_supported)},
- {"native_start_navigation_message_collection",
- "()Z",
- reinterpret_cast<void *>(
- android_location_GnssLocationProvider_start_navigation_message_collection)},
- {"native_stop_navigation_message_collection",
- "()Z",
- reinterpret_cast<void *>(
- android_location_GnssLocationProvider_stop_navigation_message_collection)},
{"native_set_supl_es",
"(I)Z",
reinterpret_cast<void *>(android_location_GnssLocationProvider_set_supl_es)},
@@ -2269,6 +2245,38 @@
reinterpret_cast<void *>(android_location_GnssGeofenceProvider_resume_geofence)},
};
+static const JNINativeMethod sMeasurementMethods[] = {
+ /* name, signature, funcPtr */
+ {"native_is_measurement_supported",
+ "()Z",
+ reinterpret_cast<void *>(
+ android_location_GnssMeasurementsProvider_is_measurement_supported)},
+ {"native_start_measurement_collection",
+ "(Z)Z",
+ reinterpret_cast<void *>(
+ android_location_GnssMeasurementsProvider_start_measurement_collection)},
+ {"native_stop_measurement_collection",
+ "()Z",
+ reinterpret_cast<void *>(
+ android_location_GnssMeasurementsProvider_stop_measurement_collection)},
+};
+
+static const JNINativeMethod sNavigationMessageMethods[] = {
+ /* name, signature, funcPtr */
+ {"native_is_navigation_message_supported",
+ "()Z",
+ reinterpret_cast<void *>(
+ android_location_GnssNavigationMessageProvider_is_navigation_message_supported)},
+ {"native_start_navigation_message_collection",
+ "()Z",
+ reinterpret_cast<void *>(
+ android_location_GnssNavigationMessageProvider_start_navigation_message_collection)},
+ {"native_stop_navigation_message_collection",
+ "()Z",
+ reinterpret_cast<void *>(
+ android_location_GnssNavigationMessageProvider_stop_navigation_message_collection)},
+};
+
int register_android_server_location_GnssLocationProvider(JNIEnv* env) {
jniRegisterNativeMethods(
env,
@@ -2280,6 +2288,16 @@
"com/android/server/location/GnssGeofenceProvider",
sGeofenceMethods,
NELEM(sGeofenceMethods));
+ jniRegisterNativeMethods(
+ env,
+ "com/android/server/location/GnssMeasurementsProvider",
+ sMeasurementMethods,
+ NELEM(sMeasurementMethods));
+ jniRegisterNativeMethods(
+ env,
+ "com/android/server/location/GnssNavigationMessageProvider",
+ sNavigationMessageMethods,
+ NELEM(sNavigationMessageMethods));
return jniRegisterNativeMethods(
env,
"com/android/server/location/GnssLocationProvider",
diff --git a/services/net/java/android/net/dns/ResolvUtil.java b/services/net/java/android/net/dns/ResolvUtil.java
index 97d20f4..a2a6615 100644
--- a/services/net/java/android/net/dns/ResolvUtil.java
+++ b/services/net/java/android/net/dns/ResolvUtil.java
@@ -62,4 +62,13 @@
final long netidForResolv = NETID_USE_LOCAL_NAMESERVERS | (long) network.netId;
return new Network((int) netidForResolv);
}
+
+ public static Network makeNetworkWithPrivateDnsBypass(Network network) {
+ return new Network(network) {
+ @Override
+ public InetAddress[] getAllByName(String host) throws UnknownHostException {
+ return blockingResolveAllLocally(network, host);
+ }
+ };
+ }
}
diff --git a/services/robotests/src/com/android/server/location/GnssMeasurementsProviderTest.java b/services/robotests/src/com/android/server/location/GnssMeasurementsProviderTest.java
new file mode 100644
index 0000000..23d6cf6
--- /dev/null
+++ b/services/robotests/src/com/android/server/location/GnssMeasurementsProviderTest.java
@@ -0,0 +1,90 @@
+package com.android.server.location;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.platform.test.annotations.Presubmit;
+
+import com.android.server.testing.FrameworkRobolectricTestRunner;
+import com.android.server.testing.SystemLoaderPackages;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+/**
+ * Unit tests for {@link GnssMeasurementsProvider}.
+ */
+@RunWith(FrameworkRobolectricTestRunner.class)
+@Config(
+ manifest = Config.NONE,
+ sdk = 27
+)
+@SystemLoaderPackages({"com.android.server.location"})
+@Presubmit
+public class GnssMeasurementsProviderTest {
+ @Mock
+ private GnssMeasurementsProvider.GnssMeasurementProviderNative mMockNative;
+ private GnssMeasurementsProvider mTestProvider;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ when(mMockNative.startMeasurementCollection(anyBoolean())).thenReturn(true);
+ when(mMockNative.stopMeasurementCollection()).thenReturn(true);
+
+ mTestProvider = new GnssMeasurementsProvider(RuntimeEnvironment.application,
+ new Handler(Looper.myLooper()), mMockNative) {
+ @Override
+ public boolean isGpsEnabled() {
+ return true;
+ }
+ };
+ }
+
+ @Test
+ public void register_nativeStarted() {
+ mTestProvider.registerWithService();
+ verify(mMockNative).startMeasurementCollection(anyBoolean());
+ }
+
+ @Test
+ public void unregister_nativeStopped() {
+ mTestProvider.registerWithService();
+ mTestProvider.unregisterFromService();
+ verify(mMockNative).stopMeasurementCollection();
+ }
+
+ @Test
+ public void isSupported_nativeIsSupported() {
+ when(mMockNative.isMeasurementSupported()).thenReturn(true);
+ assertThat(mTestProvider.isAvailableInPlatform()).isTrue();
+
+ when(mMockNative.isMeasurementSupported()).thenReturn(false);
+ assertThat(mTestProvider.isAvailableInPlatform()).isFalse();
+ }
+
+ @Test
+ public void register_resume_started() {
+ mTestProvider.registerWithService();
+ mTestProvider.resumeIfStarted();
+ verify(mMockNative, times(2)).startMeasurementCollection(anyBoolean());
+ }
+
+ @Test
+ public void unregister_resume_notStarted() {
+ mTestProvider.registerWithService();
+ mTestProvider.unregisterFromService();
+ mTestProvider.resumeIfStarted();
+ verify(mMockNative, times(1)).startMeasurementCollection(anyBoolean());
+ }
+}
diff --git a/services/robotests/src/com/android/server/location/GnssNavigationMessageProviderTest.java b/services/robotests/src/com/android/server/location/GnssNavigationMessageProviderTest.java
new file mode 100644
index 0000000..8d3de3c
--- /dev/null
+++ b/services/robotests/src/com/android/server/location/GnssNavigationMessageProviderTest.java
@@ -0,0 +1,89 @@
+package com.android.server.location;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.platform.test.annotations.Presubmit;
+
+import com.android.server.testing.FrameworkRobolectricTestRunner;
+import com.android.server.testing.SystemLoaderPackages;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.annotation.Config;
+
+/**
+ * Unit tests for {@link GnssNavigationMessageProvider}.
+ */
+@RunWith(FrameworkRobolectricTestRunner.class)
+@Config(
+ manifest = Config.NONE,
+ sdk = 27
+)
+@SystemLoaderPackages({"com.android.server.location"})
+@Presubmit
+public class GnssNavigationMessageProviderTest {
+ @Mock
+ private GnssNavigationMessageProvider.GnssNavigationMessageProviderNative mMockNative;
+ private GnssNavigationMessageProvider mTestProvider;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ when(mMockNative.startNavigationMessageCollection()).thenReturn(true);
+ when(mMockNative.stopNavigationMessageCollection()).thenReturn(true);
+
+ mTestProvider = new GnssNavigationMessageProvider(new Handler(Looper.myLooper()),
+ mMockNative) {
+ @Override
+ public boolean isGpsEnabled() {
+ return true;
+ }
+ };
+ }
+
+ @Test
+ public void register_nativeStarted() {
+ mTestProvider.registerWithService();
+ verify(mMockNative).startNavigationMessageCollection();
+ }
+
+ @Test
+ public void unregister_nativeStopped() {
+ mTestProvider.registerWithService();
+ mTestProvider.unregisterFromService();
+ verify(mMockNative).stopNavigationMessageCollection();
+ }
+
+ @Test
+ public void isSupported_nativeIsSupported() {
+ when(mMockNative.isNavigationMessageSupported()).thenReturn(true);
+ assertThat(mTestProvider.isAvailableInPlatform()).isTrue();
+
+ when(mMockNative.isNavigationMessageSupported()).thenReturn(false);
+ assertThat(mTestProvider.isAvailableInPlatform()).isFalse();
+ }
+
+ @Test
+ public void register_resume_started() {
+ mTestProvider.registerWithService();
+ mTestProvider.resumeIfStarted();
+ verify(mMockNative, times(2)).startNavigationMessageCollection();
+ }
+
+ @Test
+ public void unregister_resume_notStarted() {
+ mTestProvider.registerWithService();
+ mTestProvider.unregisterFromService();
+ mTestProvider.resumeIfStarted();
+ verify(mMockNative, times(1)).startNavigationMessageCollection();
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/FingerprintGestureDispatcherTest.java b/services/tests/servicestests/src/com/android/server/accessibility/FingerprintGestureDispatcherTest.java
index 98bf53c..6ce7bbe 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/FingerprintGestureDispatcherTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/FingerprintGestureDispatcherTest.java
@@ -17,14 +17,17 @@
package com.android.server.accessibility;
import android.accessibilityservice.FingerprintGestureController;
+import android.content.res.Resources;
import android.hardware.fingerprint.IFingerprintService;
import android.os.Handler;
+import android.os.Looper;
import android.os.Message;
import android.view.KeyEvent;
import com.android.server.accessibility.FingerprintGestureDispatcher.FingerprintGestureClient;
import org.junit.Before;
+import org.junit.BeforeClass;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -49,16 +52,27 @@
private @Mock IFingerprintService mMockFingerprintService;
private @Mock FingerprintGestureClient mNonGestureCapturingClient;
private @Mock FingerprintGestureClient mGestureCapturingClient;
- private @Mock FingerprintGestureDispatcher mFingerprintGestureDispatcher;
+ private @Mock Resources mMockResources;
+
private MessageCapturingHandler mMessageCapturingHandler;
+ private FingerprintGestureDispatcher mFingerprintGestureDispatcher;
+
+ @BeforeClass
+ public static void oneTimeInitialization() {
+ if (Looper.myLooper() == null) {
+ Looper.prepare();
+ }
+ }
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
+ // For most tests, we support fingerprint gestures
+ when(mMockResources.getBoolean(anyInt())).thenReturn(true);
mMessageCapturingHandler = new MessageCapturingHandler(
msg -> mFingerprintGestureDispatcher.handleMessage(msg));
mFingerprintGestureDispatcher = new FingerprintGestureDispatcher(mMockFingerprintService,
- new Object(), mMessageCapturingHandler);
+ mMockResources, new Object(), mMessageCapturingHandler);
when(mNonGestureCapturingClient.isCapturingFingerprintGestures()).thenReturn(false);
when(mGestureCapturingClient.isCapturingFingerprintGestures()).thenReturn(true);
}
@@ -149,10 +163,23 @@
}
@Test
- public void testIsGestureDetectionActive_dependsOnFingerprintService() throws Exception {
+ public void testIsGestureDetectionAvailable_dependsOnFingerprintService() throws Exception {
when(mMockFingerprintService.isClientActive()).thenReturn(true);
assertFalse(mFingerprintGestureDispatcher.isFingerprintGestureDetectionAvailable());
when(mMockFingerprintService.isClientActive()).thenReturn(false);
assertTrue(mFingerprintGestureDispatcher.isFingerprintGestureDetectionAvailable());
}
+
+ @Test
+ public void ifGestureDectionNotSupported_neverSaysAvailable() throws Exception {
+ when(mMockResources.getBoolean(anyInt())).thenReturn(false);
+ // Need to create a new dispatcher, since it picks up the resource value in its
+ // constructor. This is fine since hardware config values don't change dynamically.
+ FingerprintGestureDispatcher fingerprintGestureDispatcher =
+ new FingerprintGestureDispatcher(mMockFingerprintService, mMockResources,
+ new Object(), mMessageCapturingHandler);
+
+ when(mMockFingerprintService.isClientActive()).thenReturn(false);
+ assertFalse(fingerprintGestureDispatcher.isFingerprintGestureDetectionAvailable());
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java b/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java
index 592f7b1..b73ac89 100644
--- a/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/RecentTasksTest.java
@@ -21,6 +21,7 @@
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
@@ -303,6 +304,8 @@
@Test
public void testAddTaskCompatibleActivityType_expectRemove() throws Exception {
+ // Test with undefined activity type since the type is not persisted by the task persister
+ // and we want to ensure that a new task will match a restored task
Configuration config1 = new Configuration();
config1.windowConfiguration.setActivityType(ACTIVITY_TYPE_UNDEFINED);
TaskRecord task1 = createTaskBuilder(".Task1")
@@ -355,6 +358,65 @@
}
@Test
+ public void testAddTaskCompatibleWindowingMode_expectRemove() throws Exception {
+ Configuration config1 = new Configuration();
+ config1.windowConfiguration.setWindowingMode(WINDOWING_MODE_UNDEFINED);
+ TaskRecord task1 = createTaskBuilder(".Task1")
+ .setFlags(FLAG_ACTIVITY_NEW_TASK)
+ .setStack(mStack)
+ .build();
+ task1.onConfigurationChanged(config1);
+ assertTrue(task1.getWindowingMode() == WINDOWING_MODE_UNDEFINED);
+ mRecentTasks.add(task1);
+ mCallbacksRecorder.clear();
+
+ Configuration config2 = new Configuration();
+ config2.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+ TaskRecord task2 = createTaskBuilder(".Task1")
+ .setFlags(FLAG_ACTIVITY_NEW_TASK)
+ .setStack(mStack)
+ .build();
+ task2.onConfigurationChanged(config2);
+ assertTrue(task2.getWindowingMode() == WINDOWING_MODE_FULLSCREEN);
+ mRecentTasks.add(task2);
+
+ assertTrue(mCallbacksRecorder.added.size() == 1);
+ assertTrue(mCallbacksRecorder.added.contains(task2));
+ assertTrue(mCallbacksRecorder.trimmed.isEmpty());
+ assertTrue(mCallbacksRecorder.removed.size() == 1);
+ assertTrue(mCallbacksRecorder.removed.contains(task1));
+ }
+
+ @Test
+ public void testAddTaskIncompatibleWindowingMode_expectNoRemove() throws Exception {
+ Configuration config1 = new Configuration();
+ config1.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN);
+ TaskRecord task1 = createTaskBuilder(".Task1")
+ .setFlags(FLAG_ACTIVITY_NEW_TASK)
+ .setStack(mStack)
+ .build();
+ task1.onConfigurationChanged(config1);
+ assertTrue(task1.getWindowingMode() == WINDOWING_MODE_FULLSCREEN);
+ mRecentTasks.add(task1);
+
+ Configuration config2 = new Configuration();
+ config2.windowConfiguration.setWindowingMode(WINDOWING_MODE_PINNED);
+ TaskRecord task2 = createTaskBuilder(".Task1")
+ .setFlags(FLAG_ACTIVITY_NEW_TASK)
+ .setStack(mStack)
+ .build();
+ task2.onConfigurationChanged(config2);
+ assertTrue(task2.getWindowingMode() == WINDOWING_MODE_PINNED);
+ mRecentTasks.add(task2);
+
+ assertTrue(mCallbacksRecorder.added.size() == 2);
+ assertTrue(mCallbacksRecorder.added.contains(task1));
+ assertTrue(mCallbacksRecorder.added.contains(task2));
+ assertTrue(mCallbacksRecorder.trimmed.isEmpty());
+ assertTrue(mCallbacksRecorder.removed.isEmpty());
+ }
+
+ @Test
public void testUsersTasks() throws Exception {
mRecentTasks.setOnlyTestVisibleRange();
diff --git a/services/tests/servicestests/src/com/android/server/wm/BoundsAnimationControllerTests.java b/services/tests/servicestests/src/com/android/server/wm/BoundsAnimationControllerTests.java
index 6019958..ff631e7 100644
--- a/services/tests/servicestests/src/com/android/server/wm/BoundsAnimationControllerTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/BoundsAnimationControllerTests.java
@@ -545,7 +545,7 @@
.restart(BOUNDS_SMALLER_FLOATING,
false /* expectStartedAndPipModeChangedCallback */)
.end()
- .expectEnded(SCHEDULE_PIP_MODE_CHANGED, !MOVE_TO_FULLSCREEN);
+ .expectEnded(SCHEDULE_PIP_MODE_CHANGED, MOVE_TO_FULLSCREEN);
}
/** !F->!F w/ CANCEL **/
diff --git a/services/tests/servicestests/src/com/android/server/wm/RootWindowContainerTests.java b/services/tests/servicestests/src/com/android/server/wm/RootWindowContainerTests.java
index 51b019a..204e26c 100644
--- a/services/tests/servicestests/src/com/android/server/wm/RootWindowContainerTests.java
+++ b/services/tests/servicestests/src/com/android/server/wm/RootWindowContainerTests.java
@@ -24,27 +24,29 @@
public class RootWindowContainerTests extends WindowTestsBase {
@Test
public void testSetDisplayOverrideConfigurationIfNeeded() throws Exception {
- // Add first stack we expect to be updated with configuration change.
- final TaskStack stack = createTaskStackOnDisplay(mDisplayContent);
- stack.getOverrideConfiguration().windowConfiguration.setBounds(new Rect(0, 0, 5, 5));
+ synchronized (sWm.mWindowMap) {
+ // Add first stack we expect to be updated with configuration change.
+ final TaskStack stack = createTaskStackOnDisplay(mDisplayContent);
+ stack.getOverrideConfiguration().windowConfiguration.setBounds(new Rect(0, 0, 5, 5));
- // Add second task that will be set for deferred removal that should not be returned
- // with the configuration change.
- final TaskStack deferredDeletedStack = createTaskStackOnDisplay(mDisplayContent);
- deferredDeletedStack.getOverrideConfiguration().windowConfiguration.setBounds(
- new Rect(0, 0, 5, 5));
- deferredDeletedStack.mDeferRemoval = true;
+ // Add second task that will be set for deferred removal that should not be returned
+ // with the configuration change.
+ final TaskStack deferredDeletedStack = createTaskStackOnDisplay(mDisplayContent);
+ deferredDeletedStack.getOverrideConfiguration().windowConfiguration.setBounds(
+ new Rect(0, 0, 5, 5));
+ deferredDeletedStack.mDeferRemoval = true;
- final Configuration override = new Configuration(
- mDisplayContent.getOverrideConfiguration());
- override.windowConfiguration.setBounds(new Rect(0, 0, 10, 10));
+ final Configuration override = new Configuration(
+ mDisplayContent.getOverrideConfiguration());
+ override.windowConfiguration.setBounds(new Rect(0, 0, 10, 10));
- // Set display override.
- final int[] results = sWm.mRoot.setDisplayOverrideConfigurationIfNeeded(override,
- mDisplayContent.getDisplayId());
+ // Set display override.
+ final int[] results = sWm.mRoot.setDisplayOverrideConfigurationIfNeeded(override,
+ mDisplayContent.getDisplayId());
- // Ensure only first stack is returned.
- assertTrue(results.length == 1);
- assertTrue(results[0] == stack.mStackId);
+ // Ensure only first stack is returned.
+ assertTrue(results.length == 1);
+ assertTrue(results[0] == stack.mStackId);
+ }
}
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
index 4668ed4..d0a656c 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
@@ -343,7 +343,7 @@
parser.setInput(new BufferedInputStream(
new ByteArrayInputStream(baos.toByteArray())), null);
parser.nextTag();
- service.readXml(parser);
+ service.readXml(parser, null);
verifyExpectedApprovedEntries(service);
assertFalse(service.isPackageOrComponentAllowed("this.is.a.package.name", 0));
@@ -665,7 +665,7 @@
parser.setInput(new BufferedInputStream(
new ByteArrayInputStream(xml.toString().getBytes())), null);
parser.nextTag();
- service.readXml(parser);
+ service.readXml(parser, null);
}
private void addExpectedServices(final ManagedServices service, final List<String> packages,
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
index a8b9dff..f9a4f78 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationAssistantsTest.java
@@ -124,7 +124,7 @@
parser.setInput(new BufferedInputStream(
new ByteArrayInputStream(xml.toString().getBytes())), null);
parser.nextTag();
- mAssistants.readXml(parser);
+ mAssistants.readXml(parser, null);
verify(mNm, never()).readDefaultAssistant(anyInt());
verify(mAssistants, times(1)).addApprovedList(
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationComparatorTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationComparatorTest.java
index 30fae01..7ee0501 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationComparatorTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationComparatorTest.java
@@ -138,12 +138,14 @@
mRecordInlineReply.setUserImportance(NotificationManager.IMPORTANCE_HIGH);
mRecordInlineReply.setPackagePriority(Notification.PRIORITY_MAX);
- Notification n5 = new Notification.Builder(mContext, TEST_CHANNEL_ID)
- .setCategory(Notification.CATEGORY_MESSAGE).build();
- mRecordSms = new NotificationRecord(mContext, new StatusBarNotification(smsPkg,
- smsPkg, 1, "sms", smsUid, smsUid, n5, new UserHandle(userId),
- "", 1299), getDefaultChannel());
- mRecordSms.setUserImportance(NotificationManager.IMPORTANCE_DEFAULT);
+ if (smsPkg != null) {
+ Notification n5 = new Notification.Builder(mContext, TEST_CHANNEL_ID)
+ .setCategory(Notification.CATEGORY_MESSAGE).build();
+ mRecordSms = new NotificationRecord(mContext, new StatusBarNotification(smsPkg,
+ smsPkg, 1, "sms", smsUid, smsUid, n5, new UserHandle(userId),
+ "", 1299), getDefaultChannel());
+ mRecordSms.setUserImportance(NotificationManager.IMPORTANCE_DEFAULT);
+ }
Notification n6 = new Notification.Builder(mContext, TEST_CHANNEL_ID).build();
mRecordStarredContact = new NotificationRecord(mContext, new StatusBarNotification(pkg2,
@@ -230,7 +232,9 @@
expected.add(mRecordColorized);
expected.add(mRecordHighCall);
expected.add(mRecordInlineReply);
- expected.add(mRecordSms);
+ if (mRecordSms != null) {
+ expected.add(mRecordSms);
+ }
expected.add(mRecordStarredContact);
expected.add(mRecordContact);
expected.add(mRecordEmail);
@@ -253,7 +257,9 @@
public void testMessaging() throws Exception {
NotificationComparator comp = new NotificationComparator(mContext);
assertTrue(comp.isImportantMessaging(mRecordInlineReply));
- assertTrue(comp.isImportantMessaging(mRecordSms));
+ if (mRecordSms != null) {
+ assertTrue(comp.isImportantMessaging(mRecordSms));
+ }
assertFalse(comp.isImportantMessaging(mRecordEmail));
assertFalse(comp.isImportantMessaging(mRecordCheater));
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 0c2928a9..eb1c997 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -80,6 +80,7 @@
import android.content.pm.IPackageManager;
import android.content.pm.PackageManager;
import android.content.pm.ParceledListSlice;
+import android.content.res.Resources;
import android.graphics.Color;
import android.media.AudioAttributes;
import android.media.AudioManager;
@@ -105,6 +106,7 @@
import android.util.ArrayMap;
import android.util.AtomicFile;
+import com.android.internal.R;
import com.android.internal.statusbar.NotificationVisibility;
import com.android.server.UiServiceTestCase;
import com.android.server.lights.Light;
@@ -160,6 +162,8 @@
@Mock
ActivityManager mActivityManager;
NotificationManagerService.WorkerHandler mHandler;
+ @Mock
+ Resources mResources;
private NotificationChannel mTestNotificationChannel = new NotificationChannel(
TEST_CHANNEL_ID, TEST_CHANNEL_ID, NotificationManager.IMPORTANCE_DEFAULT);
@@ -391,7 +395,7 @@
public void testCreateNotificationChannels_NullChannelThrowsException() throws Exception {
try {
mBinderService.createNotificationChannels(PKG,
- new ParceledListSlice(Arrays.asList(null)));
+ new ParceledListSlice(Arrays.asList((Object[])null)));
fail("Exception should be thrown immediately.");
} catch (NullPointerException e) {
// pass
@@ -2197,9 +2201,9 @@
+ "</notification-policy>";
mService.readPolicyXml(
new BufferedInputStream(new ByteArrayInputStream(upgradeXml.getBytes())), false);
- verify(mListeners, times(1)).readXml(any());
- verify(mConditionProviders, times(1)).readXml(any());
- verify(mAssistants, times(1)).readXml(any());
+ verify(mListeners, times(1)).readXml(any(), any());
+ verify(mConditionProviders, times(1)).readXml(any(), any());
+ verify(mAssistants, times(1)).readXml(any(), any());
// numbers are inflated for setup
verify(mListeners, times(1)).migrateToXml();
@@ -2216,9 +2220,9 @@
+ "</notification-policy>";
mService.readPolicyXml(
new BufferedInputStream(new ByteArrayInputStream(preupgradeXml.getBytes())), false);
- verify(mListeners, never()).readXml(any());
- verify(mConditionProviders, never()).readXml(any());
- verify(mAssistants, never()).readXml(any());
+ verify(mListeners, never()).readXml(any(), any());
+ verify(mConditionProviders, never()).readXml(any(), any());
+ verify(mAssistants, never()).readXml(any(), any());
// numbers are inflated for setup
verify(mListeners, times(2)).migrateToXml();
@@ -2780,4 +2784,70 @@
verify(mListeners, times(1)).notifyHiddenLocked(captor.capture());
assertEquals(0, captor.getValue().size());
}
+
+ @Test
+ public void testCanUseManagedServicesLowRamNoWatchNullPkg() {
+ when(mPackageManagerClient.hasSystemFeature(FEATURE_WATCH)).thenReturn(false);
+ when(mActivityManager.isLowRamDevice()).thenReturn(true);
+ when(mResources.getStringArray(R.array.config_allowedManagedServicesOnLowRamDevices))
+ .thenReturn(new String[] {"a", "b", "c"});
+ when(mContext.getResources()).thenReturn(mResources);
+
+ assertEquals(false, mService.canUseManagedServices(null));
+ }
+
+ @Test
+ public void testCanUseManagedServicesLowRamNoWatchValidPkg() {
+ when(mPackageManagerClient.hasSystemFeature(FEATURE_WATCH)).thenReturn(false);
+ when(mActivityManager.isLowRamDevice()).thenReturn(true);
+ when(mResources.getStringArray(R.array.config_allowedManagedServicesOnLowRamDevices))
+ .thenReturn(new String[] {"a", "b", "c"});
+ when(mContext.getResources()).thenReturn(mResources);
+
+ assertEquals(true, mService.canUseManagedServices("b"));
+ }
+
+ @Test
+ public void testCanUseManagedServicesLowRamNoWatchNoValidPkg() {
+ when(mPackageManagerClient.hasSystemFeature(FEATURE_WATCH)).thenReturn(false);
+ when(mActivityManager.isLowRamDevice()).thenReturn(true);
+ when(mResources.getStringArray(R.array.config_allowedManagedServicesOnLowRamDevices))
+ .thenReturn(new String[] {"a", "b", "c"});
+ when(mContext.getResources()).thenReturn(mResources);
+
+ assertEquals(false, mService.canUseManagedServices("d"));
+ }
+
+ @Test
+ public void testCanUseManagedServicesLowRamWatchNoValidPkg() {
+ when(mPackageManagerClient.hasSystemFeature(FEATURE_WATCH)).thenReturn(true);
+ when(mActivityManager.isLowRamDevice()).thenReturn(true);
+ when(mResources.getStringArray(R.array.config_allowedManagedServicesOnLowRamDevices))
+ .thenReturn(new String[] {"a", "b", "c"});
+ when(mContext.getResources()).thenReturn(mResources);
+
+ assertEquals(true, mService.canUseManagedServices("d"));
+ }
+
+ @Test
+ public void testCanUseManagedServicesNoLowRamNoWatchValidPkg() {
+ when(mPackageManagerClient.hasSystemFeature(FEATURE_WATCH)).thenReturn(false);
+ when(mActivityManager.isLowRamDevice()).thenReturn(false);
+ when(mResources.getStringArray(R.array.config_allowedManagedServicesOnLowRamDevices))
+ .thenReturn(new String[] {"a", "b", "c"});
+ when(mContext.getResources()).thenReturn(mResources);
+
+ assertEquals(true, mService.canUseManagedServices("d"));
+ }
+
+ @Test
+ public void testCanUseManagedServicesNoLowRamWatchValidPkg() {
+ when(mPackageManagerClient.hasSystemFeature(FEATURE_WATCH)).thenReturn(true);
+ when(mActivityManager.isLowRamDevice()).thenReturn(false);
+ when(mResources.getStringArray(R.array.config_allowedManagedServicesOnLowRamDevices))
+ .thenReturn(new String[] {"a", "b", "c"});
+ when(mContext.getResources()).thenReturn(mResources);
+
+ assertEquals(true, mService.canUseManagedServices("d"));
+ }
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationTest.java
index 9f7205b..d846d21 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationTest.java
@@ -25,6 +25,8 @@
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyInt;
import android.app.ActivityManager;
import android.app.Notification;
@@ -33,6 +35,8 @@
import android.app.RemoteInput;
import android.content.Context;
import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.drawable.Icon;
@@ -57,20 +61,29 @@
@Mock
ActivityManager mAm;
+ @Mock
+ Resources mResources;
+
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
}
@Test
- public void testStripsExtendersInLowRamMode() {
+ public void testStripsExtendersInLowRamModeNoWhitelistNoTv() {
Notification.Builder nb = new Notification.Builder(mContext, "channel");
nb.extend(new Notification.CarExtender().setColor(Color.RED));
nb.extend(new Notification.TvExtender().setChannelId("different channel"));
nb.extend(new Notification.WearableExtender().setDismissalId("dismiss"));
Notification before = nb.build();
- Notification after = Notification.Builder.maybeCloneStrippedForDelivery(before, true);
+ // No whitelist
+ Context context = spy(getContext());
+ when(context.getResources()).thenReturn(mResources);
+ when(mResources.getStringArray(anyInt())).thenReturn(new String[0]);
+
+ Notification after = Notification.Builder.maybeCloneStrippedForDelivery(before, true,
+ context);
assertEquals("different channel", new Notification.TvExtender(before).getChannelId());
assertNull(new Notification.TvExtender(after).getChannelId());
@@ -83,8 +96,34 @@
}
@Test
+ public void testStripsExtendersInLowRamModeHasWhitelist() {
+ Notification.Builder nb = new Notification.Builder(mContext, "channel");
+ nb.extend(new Notification.CarExtender().setColor(Color.RED));
+ nb.extend(new Notification.TvExtender().setChannelId("different channel"));
+ nb.extend(new Notification.WearableExtender().setDismissalId("dismiss"));
+ Notification before = nb.build();
+
+ // Has whitelist
+ Context context = spy(mContext);
+ when(context.getResources()).thenReturn(mResources);
+ when(mResources.getStringArray(anyInt())).thenReturn(new String[1]);
+
+ Notification after = Notification.Builder.maybeCloneStrippedForDelivery(before, true,
+ context);
+
+ assertEquals("different channel", new Notification.TvExtender(before).getChannelId());
+ assertEquals("different channel", new Notification.TvExtender(after).getChannelId());
+
+ assertEquals(Color.RED, new Notification.CarExtender(before).getColor());
+ assertEquals(Color.RED, new Notification.CarExtender(after).getColor());
+
+ assertEquals("dismiss", new Notification.WearableExtender(before).getDismissalId());
+ assertEquals("dismiss", new Notification.WearableExtender(after).getDismissalId());
+ }
+
+ @Test
public void testStripsRemoteViewsInLowRamMode() {
- Context context = spy(getContext());
+ Context context = spy(mContext);
ApplicationInfo ai = new ApplicationInfo();
ai.targetSdkVersion = Build.VERSION_CODES.M;
when(context.getApplicationInfo()).thenReturn(ai);
@@ -97,7 +136,8 @@
.setStyle(style)
.build();
- Notification after = Notification.Builder.maybeCloneStrippedForDelivery(before, true);
+ Notification after = Notification.Builder.maybeCloneStrippedForDelivery(before, true,
+ mContext);
assertNotNull(before.contentView);
assertNotNull(before.bigContentView);
assertNotNull(before.headsUpContentView);
@@ -113,7 +153,8 @@
nb.extend(new Notification.TvExtender().setChannelId("different channel"));
nb.extend(new Notification.WearableExtender().setDismissalId("dismiss"));
Notification before = nb.build();
- Notification after = Notification.Builder.maybeCloneStrippedForDelivery(before, false);
+ Notification after = Notification.Builder.maybeCloneStrippedForDelivery(before, false,
+ mContext);
assertTrue(before == after);
diff --git a/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java
index d49ba3e..43a4e27 100644
--- a/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/slice/SliceManagerServiceTest.java
@@ -93,7 +93,7 @@
mService.pinSlice("pkg", TEST_URI, EMPTY_SPECS, mToken);
mService.pinSlice("pkg", TEST_URI, EMPTY_SPECS, mToken);
- verify(mService, times(1)).createPinnedSlice(eq(TEST_URI), eq("pkg"));
+ verify(mService, times(1)).createPinnedSlice(eq(TEST_URI), anyString());
}
@Test
@@ -126,4 +126,4 @@
verify(mContextSpy).checkPermission(eq("perm2"), eq(Process.myPid()), eq(Process.myUid()));
}
-}
\ No newline at end of file
+}
diff --git a/services/usage/java/com/android/server/usage/AppStandbyController.java b/services/usage/java/com/android/server/usage/AppStandbyController.java
index 920a605..97c5ac9 100644
--- a/services/usage/java/com/android/server/usage/AppStandbyController.java
+++ b/services/usage/java/com/android/server/usage/AppStandbyController.java
@@ -81,7 +81,7 @@
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.UserHandle;
-import android.provider.Settings;
+import android.provider.Settings.Global;
import android.telephony.TelephonyManager;
import android.util.ArraySet;
import android.util.KeyValueListParser;
@@ -1439,8 +1439,10 @@
boolean isAppIdleEnabled() {
final boolean buildFlag = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_enableAutoPowerModes);
- final boolean runtimeFlag = Settings.Global.getInt(mContext.getContentResolver(),
- Settings.Global.APP_STANDBY_ENABLED, 1) == 1;
+ final boolean runtimeFlag = Global.getInt(mContext.getContentResolver(),
+ Global.APP_STANDBY_ENABLED, 1) == 1
+ && Global.getInt(mContext.getContentResolver(),
+ Global.ADAPTIVE_BATTERY_MANAGEMENT_ENABLED, 1) == 1;
return buildFlag && runtimeFlag;
}
@@ -1489,8 +1491,8 @@
}
String getAppIdleSettings() {
- return Settings.Global.getString(mContext.getContentResolver(),
- Settings.Global.APP_IDLE_CONSTANTS);
+ return Global.getString(mContext.getContentResolver(),
+ Global.APP_IDLE_CONSTANTS);
}
}
@@ -1610,7 +1612,7 @@
};
/**
- * Observe settings changes for {@link Settings.Global#APP_IDLE_CONSTANTS}.
+ * Observe settings changes for {@link Global#APP_IDLE_CONSTANTS}.
*/
private class SettingsObserver extends ContentObserver {
/**
@@ -1650,10 +1652,11 @@
}
void registerObserver() {
- mContext.getContentResolver().registerContentObserver(Settings.Global.getUriFor(
- Settings.Global.APP_IDLE_CONSTANTS), false, this);
- mContext.getContentResolver().registerContentObserver(Settings.Global.getUriFor(
- Settings.Global.APP_STANDBY_ENABLED), false, this);
+ final ContentResolver cr = mContext.getContentResolver();
+ cr.registerContentObserver(Global.getUriFor(Global.APP_IDLE_CONSTANTS), false, this);
+ cr.registerContentObserver(Global.getUriFor(Global.APP_STANDBY_ENABLED), false, this);
+ cr.registerContentObserver(Global.getUriFor(Global.ADAPTIVE_BATTERY_MANAGEMENT_ENABLED),
+ false, this);
}
@Override
@@ -1665,11 +1668,14 @@
void updateSettings() {
if (DEBUG) {
Slog.d(TAG,
- "appidle=" + Settings.Global.getString(mContext.getContentResolver(),
- Settings.Global.APP_STANDBY_ENABLED));
- Slog.d(TAG, "appidleconstants=" + Settings.Global.getString(
+ "appidle=" + Global.getString(mContext.getContentResolver(),
+ Global.APP_STANDBY_ENABLED));
+ Slog.d(TAG,
+ "adaptivebat=" + Global.getString(mContext.getContentResolver(),
+ Global.ADAPTIVE_BATTERY_MANAGEMENT_ENABLED));
+ Slog.d(TAG, "appidleconstants=" + Global.getString(
mContext.getContentResolver(),
- Settings.Global.APP_IDLE_CONSTANTS));
+ Global.APP_IDLE_CONSTANTS));
}
// Check if app_idle_enabled has changed
setAppIdleEnabled(mInjector.isAppIdleEnabled());
diff --git a/services/usage/java/com/android/server/usage/IntervalStats.java b/services/usage/java/com/android/server/usage/IntervalStats.java
index c914689..0dce738 100644
--- a/services/usage/java/com/android/server/usage/IntervalStats.java
+++ b/services/usage/java/com/android/server/usage/IntervalStats.java
@@ -16,6 +16,7 @@
package com.android.server.usage;
import android.app.usage.ConfigurationStats;
+import android.app.usage.EventList;
import android.app.usage.EventStats;
import android.app.usage.TimeSparseArray;
import android.app.usage.UsageEvents;
@@ -37,7 +38,7 @@
public final ArrayMap<String, UsageStats> packageStats = new ArrayMap<>();
public final ArrayMap<Configuration, ConfigurationStats> configurations = new ArrayMap<>();
public Configuration activeConfiguration;
- public TimeSparseArray<UsageEvents.Event> events;
+ public EventList events;
// A string cache. This is important as when we're parsing XML files, we don't want to
// keep hundreds of strings that have the same contents. We will read the string
diff --git a/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java b/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
index fe3a884..aa832ad 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsXmlV1.java
@@ -22,12 +22,11 @@
import org.xmlpull.v1.XmlSerializer;
import android.app.usage.ConfigurationStats;
-import android.app.usage.TimeSparseArray;
+import android.app.usage.EventList;
import android.app.usage.UsageEvents;
import android.app.usage.UsageStats;
import android.content.res.Configuration;
import android.util.ArrayMap;
-import android.util.Pair;
import java.io.IOException;
import java.net.ProtocolException;
@@ -193,9 +192,9 @@
}
if (statsOut.events == null) {
- statsOut.events = new TimeSparseArray<>();
+ statsOut.events = new EventList();
}
- statsOut.events.put(event.mTimeStamp, event);
+ statsOut.events.insert(event);
}
private static void writeUsageStats(XmlSerializer xml, final IntervalStats stats,
@@ -411,7 +410,7 @@
xml.startTag(null, EVENT_LOG_TAG);
final int eventCount = stats.events != null ? stats.events.size() : 0;
for (int i = 0; i < eventCount; i++) {
- writeEvent(xml, stats, stats.events.valueAt(i));
+ writeEvent(xml, stats, stats.events.get(i));
}
xml.endTag(null, EVENT_LOG_TAG);
}
diff --git a/services/usage/java/com/android/server/usage/UserUsageStatsService.java b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
index d9fc066..9cb98f3 100644
--- a/services/usage/java/com/android/server/usage/UserUsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UserUsageStatsService.java
@@ -17,15 +17,14 @@
package com.android.server.usage;
import android.app.usage.ConfigurationStats;
+import android.app.usage.EventList;
import android.app.usage.EventStats;
-import android.app.usage.TimeSparseArray;
import android.app.usage.UsageEvents;
import android.app.usage.UsageStats;
import android.app.usage.UsageStatsManager;
import android.content.res.Configuration;
import android.os.SystemClock;
import android.content.Context;
-import android.text.format.DateFormat;
import android.text.format.DateUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -174,10 +173,10 @@
// Add the event to the daily list.
if (currentDailyStats.events == null) {
- currentDailyStats.events = new TimeSparseArray<>();
+ currentDailyStats.events = new EventList();
}
if (event.mEventType != UsageEvents.Event.SYSTEM_INTERACTION) {
- currentDailyStats.events.put(event.mTimeStamp, event);
+ currentDailyStats.events.insert(event);
}
boolean incrementAppLaunch = false;
@@ -367,18 +366,14 @@
return;
}
- final int startIndex = stats.events.closestIndexOnOrAfter(beginTime);
- if (startIndex < 0) {
- return;
- }
-
+ final int startIndex = stats.events.firstIndexOnOrAfter(beginTime);
final int size = stats.events.size();
for (int i = startIndex; i < size; i++) {
- if (stats.events.keyAt(i) >= endTime) {
+ if (stats.events.get(i).mTimeStamp >= endTime) {
return;
}
- UsageEvents.Event event = stats.events.valueAt(i);
+ UsageEvents.Event event = stats.events.get(i);
if (obfuscateInstantApps) {
event = event.getObfuscatedIfInstantApp();
}
@@ -410,18 +405,14 @@
return;
}
- final int startIndex = stats.events.closestIndexOnOrAfter(beginTime);
- if (startIndex < 0) {
- return;
- }
-
+ final int startIndex = stats.events.firstIndexOnOrAfter(beginTime);
final int size = stats.events.size();
for (int i = startIndex; i < size; i++) {
- if (stats.events.keyAt(i) >= endTime) {
+ if (stats.events.get(i).mTimeStamp >= endTime) {
return;
}
- final UsageEvents.Event event = stats.events.valueAt(i);
+ final UsageEvents.Event event = stats.events.get(i);
if (!packageName.equals(event.mPackage)) {
continue;
}
@@ -633,18 +624,14 @@
return;
}
- final int startIndex = stats.events.closestIndexOnOrAfter(beginTime);
- if (startIndex < 0) {
- return;
- }
-
+ final int startIndex = stats.events.firstIndexOnOrAfter(beginTime);
final int size = stats.events.size();
for (int i = startIndex; i < size; i++) {
- if (stats.events.keyAt(i) >= endTime) {
+ if (stats.events.get(i).mTimeStamp >= endTime) {
return;
}
- UsageEvents.Event event = stats.events.valueAt(i);
+ UsageEvents.Event event = stats.events.get(i);
if (pkg != null && !pkg.equals(event.mPackage)) {
continue;
}
@@ -779,10 +766,10 @@
if (!skipEvents) {
pw.println("events");
pw.increaseIndent();
- final TimeSparseArray<UsageEvents.Event> events = stats.events;
+ final EventList events = stats.events;
final int eventCount = events != null ? events.size() : 0;
for (int i = 0; i < eventCount; i++) {
- final UsageEvents.Event event = events.valueAt(i);
+ final UsageEvents.Event event = events.get(i);
if (pkg != null && !pkg.equals(event.mPackage)) {
continue;
}
diff --git a/telephony/java/android/telephony/PhoneStateListener.java b/telephony/java/android/telephony/PhoneStateListener.java
index 0ff2982..006d7ab9 100644
--- a/telephony/java/android/telephony/PhoneStateListener.java
+++ b/telephony/java/android/telephony/PhoneStateListener.java
@@ -16,6 +16,7 @@
package android.telephony;
+import android.annotation.NonNull;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
@@ -204,6 +205,16 @@
public static final int LISTEN_VOLTE_STATE = 0x00004000;
/**
+ * Listen for OEM hook raw event
+ *
+ * @see #onOemHookRawEvent
+ * @hide
+ * @deprecated OEM needs a vendor-extension hal and their apps should use that instead
+ */
+ @Deprecated
+ public static final int LISTEN_OEM_HOOK_RAW_EVENT = 0x00008000;
+
+ /**
* Listen for carrier network changes indicated by a carrier app.
*
* @see #onCarrierNetworkRequest
@@ -367,6 +378,9 @@
case LISTEN_USER_MOBILE_DATA_STATE:
PhoneStateListener.this.onUserMobileDataStateChanged((boolean)msg.obj);
break;
+ case LISTEN_OEM_HOOK_RAW_EVENT:
+ PhoneStateListener.this.onOemHookRawEvent((byte[])msg.obj);
+ break;
case LISTEN_CARRIER_NETWORK_CHANGE:
PhoneStateListener.this.onCarrierNetworkChange((boolean)msg.obj);
break;
@@ -578,7 +592,18 @@
* @param configs List of the current {@link PhysicalChannelConfig}s
* @hide
*/
- public void onPhysicalChannelConfigurationChanged(List<PhysicalChannelConfig> configs) {
+ public void onPhysicalChannelConfigurationChanged(
+ @NonNull List<PhysicalChannelConfig> configs) {
+ // default implementation empty
+ }
+
+ /**
+ * Callback invoked when OEM hook raw event is received. Requires
+ * the READ_PRIVILEGED_PHONE_STATE permission.
+ * @param rawData is the byte array of the OEM hook raw data.
+ * @hide
+ */
+ public void onOemHookRawEvent(byte[] rawData) {
// default implementation empty
}
@@ -698,6 +723,10 @@
send(LISTEN_USER_MOBILE_DATA_STATE, 0, 0, enabled);
}
+ public void onOemHookRawEvent(byte[] rawData) {
+ send(LISTEN_OEM_HOOK_RAW_EVENT, 0, 0, rawData);
+ }
+
public void onCarrierNetworkChange(boolean active) {
send(LISTEN_CARRIER_NETWORK_CHANGE, 0, 0, active);
}
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index 3880f2f..01fb299 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -2786,6 +2786,22 @@
}
/**
+ * Test method to reload the UICC profile.
+ *
+ * @hide
+ */
+ @TestApi
+ @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
+ public void refreshUiccProfile() {
+ try {
+ ITelephony telephony = getITelephony();
+ telephony.refreshUiccProfile(mSubId);
+ } catch (RemoteException ex) {
+ Rlog.w(TAG, "RemoteException", ex);
+ }
+ }
+
+ /**
* Map logicalSlot to physicalSlot, and activate the physicalSlot if it is inactive. For
* example, passing the physicalSlots array [1, 0] means mapping the first item 1, which is
* physical slot index 1, to the logical slot 0; and mapping the second item 0, which is
@@ -6479,6 +6495,29 @@
return retVal;
}
+ /**
+ * Returns the result and response from RIL for oem request
+ *
+ * @param oemReq the data is sent to ril.
+ * @param oemResp the respose data from RIL.
+ * @return negative value request was not handled or get error
+ * 0 request was handled succesfully, but no response data
+ * positive value success, data length of response
+ * @hide
+ * @deprecated OEM needs a vendor-extension hal and their apps should use that instead
+ */
+ @Deprecated
+ public int invokeOemRilRequestRaw(byte[] oemReq, byte[] oemResp) {
+ try {
+ ITelephony telephony = getITelephony();
+ if (telephony != null)
+ return telephony.invokeOemRilRequestRaw(oemReq, oemResp);
+ } catch (RemoteException ex) {
+ } catch (NullPointerException ex) {
+ }
+ return -1;
+ }
+
/** @hide */
@SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
diff --git a/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl b/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl
index 1cfe8c2..0d315e5 100644
--- a/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl
+++ b/telephony/java/com/android/internal/telephony/IPhoneStateListener.aidl
@@ -47,6 +47,7 @@
void onVoLteServiceStateChanged(in VoLteServiceState lteState);
void onVoiceActivationStateChanged(int activationState);
void onDataActivationStateChanged(int activationState);
+ void onOemHookRawEvent(in byte[] rawData);
void onCarrierNetworkChange(in boolean active);
void onUserMobileDataStateChanged(in boolean enabled);
}
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index 7e8b2de..639dd74 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -1071,6 +1071,17 @@
in List<String> cdmaNonRoamingList);
/**
+ * Returns the result and response from RIL for oem request
+ *
+ * @param oemReq the data is sent to ril.
+ * @param oemResp the respose data from RIL.
+ * @return negative value request was not handled or get error
+ * 0 request was handled succesfully, but no response data
+ * positive value success, data length of response
+ */
+ int invokeOemRilRequestRaw(in byte[] oemReq, out byte[] oemResp);
+
+ /**
* Check if any mobile Radios need to be shutdown.
*
* @return true is any mobile radio needs to be shutdown
@@ -1509,4 +1520,10 @@
* A test API to return installed carrier id list version.
*/
int getCarrierIdListVersion(int subId);
+
+ /**
+ * A test API to reload the UICC profile.
+ * @hide
+ */
+ void refreshUiccProfile(int subId);
}
diff --git a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
index 06dc13e..0127db9 100644
--- a/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephonyRegistry.aidl
@@ -70,6 +70,7 @@
void notifyVoLteServiceStateChanged(in VoLteServiceState lteState);
void notifySimActivationStateChangedForPhoneId(in int phoneId, in int subId,
int activationState, int activationType);
+ void notifyOemHookRawEventForSubscriber(in int subId, in byte[] rawData);
void notifySubscriptionInfoChanged();
void notifyCarrierNetworkChange(in boolean active);
void notifyUserMobileDataStateChangedForPhoneId(in int phoneId, in int subId, in boolean state);
diff --git a/tests/net/java/android/net/apf/ApfTest.java b/tests/net/java/android/net/apf/ApfTest.java
index f8a4132..ed9cbab 100644
--- a/tests/net/java/android/net/apf/ApfTest.java
+++ b/tests/net/java/android/net/apf/ApfTest.java
@@ -150,15 +150,24 @@
assertVerdict(DROP, program, packet);
}
- private void assertDataMemoryContents(
- int expected, byte[] program, byte[] packet, byte[] data, byte[] expected_data)
- throws IllegalInstructionException, Exception {
+ private void assertProgramEquals(byte[] expected, byte[] program) throws AssertionError {
+ // assertArrayEquals() would only print one byte, making debugging difficult.
+ if (!java.util.Arrays.equals(expected, program)) {
+ throw new AssertionError(
+ "\nexpected: " + HexDump.toHexString(expected) +
+ "\nactual: " + HexDump.toHexString(program));
+ }
+ }
+
+ private void assertDataMemoryContents(
+ int expected, byte[] program, byte[] packet, byte[] data, byte[] expected_data)
+ throws IllegalInstructionException, Exception {
assertReturnCodesEqual(expected, apfSimulate(program, packet, data, 0 /* filterAge */));
// assertArrayEquals() would only print one byte, making debugging difficult.
if (!java.util.Arrays.equals(expected_data, data)) {
throw new Exception(
- "program: " + HexDump.toHexString(program) +
+ "\nprogram: " + HexDump.toHexString(program) +
"\ndata memory: " + HexDump.toHexString(data) +
"\nexpected: " + HexDump.toHexString(expected_data));
}
@@ -622,6 +631,64 @@
}
}
+ /**
+ * Test that the generator emits immediates using the shortest possible encoding.
+ */
+ @Test
+ public void testImmediateEncoding() throws IllegalInstructionException {
+ final int LI_OPCODE = 13 << 3;
+ ApfGenerator gen;
+
+ // 0-byte immediate: li R0, 0
+ gen = new ApfGenerator(3);
+ gen.addLoadImmediate(Register.R0, 0);
+ assertProgramEquals(new byte[]{LI_OPCODE | (0 << 1)}, gen.generate());
+
+ // 1-byte immediate: li R0, 42
+ gen = new ApfGenerator(3);
+ gen.addLoadImmediate(Register.R0, 42);
+ assertProgramEquals(new byte[]{LI_OPCODE | (1 << 1), 42}, gen.generate());
+
+ // 2-byte immediate: li R1, 0x1234
+ gen = new ApfGenerator(3);
+ gen.addLoadImmediate(Register.R1, 0x1234);
+ assertProgramEquals(new byte[]{LI_OPCODE | (2 << 1) | 1 , 0x12, 0x34}, gen.generate());
+
+ // 4-byte immediate: li R0, 0x12345678
+ gen = new ApfGenerator(3);
+ gen.addLoadImmediate(Register.R0, 0x12345678);
+ assertProgramEquals(
+ new byte[]{LI_OPCODE | (3 << 1), 0x12, 0x34, 0x56, 0x78},
+ gen.generate());
+ }
+
+ /**
+ * Test that the generator emits negative immediates using the shortest possible encoding.
+ */
+ @Test
+ public void testNegativeImmediateEncoding() throws IllegalInstructionException {
+ final int LI_OPCODE = 13 << 3;
+ ApfGenerator gen;
+
+ // 1-byte negative immediate: li R0, -42
+ gen = new ApfGenerator(3);
+ gen.addLoadImmediate(Register.R0, -42);
+ assertProgramEquals(new byte[]{LI_OPCODE | (1 << 1), -42}, gen.generate());
+
+ // 2-byte negative immediate: li R1, -0x1234
+ gen = new ApfGenerator(3);
+ gen.addLoadImmediate(Register.R1, -0x1122);
+ assertProgramEquals(new byte[]{LI_OPCODE | (2 << 1) | 1, (byte)0xEE, (byte)0xDE},
+ gen.generate());
+
+ // 4-byte negative immediate: li R0, -0x11223344
+ gen = new ApfGenerator(3);
+ gen.addLoadImmediate(Register.R0, -0x11223344);
+ assertProgramEquals(
+ new byte[]{LI_OPCODE | (3 << 1), (byte)0xEE, (byte)0xDD, (byte)0xCC, (byte)0xBC},
+ gen.generate());
+ }
+
@Test
public void testApfDataWrite() throws IllegalInstructionException, Exception {
byte[] packet = new byte[MIN_PKT_SIZE];
diff --git a/tools/aapt2/ResourceValues.cpp b/tools/aapt2/ResourceValues.cpp
index 6f213e1..34b46c5 100644
--- a/tools/aapt2/ResourceValues.cpp
+++ b/tools/aapt2/ResourceValues.cpp
@@ -117,8 +117,7 @@
bool Reference::Flatten(android::Res_value* out_value) const {
const ResourceId resid = id.value_or_default(ResourceId(0));
- const bool dynamic = resid.is_valid_dynamic() && resid.package_id() != kFrameworkPackageId &&
- resid.package_id() < kAppPackageId;
+ const bool dynamic = resid.is_valid_dynamic() && is_dynamic;
if (reference_type == Reference::Type::kResource) {
if (dynamic) {
diff --git a/tools/aapt2/ResourceValues.h b/tools/aapt2/ResourceValues.h
index 6371c4c..168ad61 100644
--- a/tools/aapt2/ResourceValues.h
+++ b/tools/aapt2/ResourceValues.h
@@ -157,6 +157,7 @@
Maybe<ResourceId> id;
Reference::Type reference_type;
bool private_reference = false;
+ bool is_dynamic = false;
Reference();
explicit Reference(const ResourceNameRef& n, Type type = Type::kResource);
diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp
index b90e7b3..0910040 100644
--- a/tools/aapt2/cmd/Link.cpp
+++ b/tools/aapt2/cmd/Link.cpp
@@ -129,6 +129,12 @@
// Stable ID options.
std::unordered_map<ResourceName, ResourceId> stable_id_map;
Maybe<std::string> resource_id_map_path;
+
+ // When 'true', allow reserved package IDs to be used for applications. Pre-O, the platform
+ // treats negative resource IDs [those with a package ID of 0x80 or higher] as invalid.
+ // In order to work around this limitation, we allow the use of traditionally reserved
+ // resource IDs [those between 0x02 and 0x7E].
+ bool allow_reserved_package_id = false;
};
class LinkContext : public IAaptContext {
@@ -899,9 +905,7 @@
// Capture the shared libraries so that the final resource table can be properly flattened
// with support for shared libraries.
for (auto& entry : asset_source->GetAssignedPackageIds()) {
- if (entry.first > kFrameworkPackageId && entry.first < kAppPackageId) {
- final_table_.included_packages_[entry.first] = entry.second;
- } else if (entry.first == kAppPackageId) {
+ if (entry.first == kAppPackageId) {
// Capture the included base feature package.
included_feature_base_ = entry.second;
} else if (entry.first == kFrameworkPackageId) {
@@ -915,6 +919,8 @@
// android:versionCode from the framework AndroidManifest.xml.
ExtractCompileSdkVersions(asset_source->GetAssetManager());
}
+ } else if (asset_source->IsPackageDynamic(entry.first)) {
+ final_table_.included_packages_[entry.first] = entry.second;
}
}
@@ -1599,7 +1605,15 @@
// If required, the package name is modifed before flattening, and then modified back
// to its original name.
ResourceTablePackage* package_to_rewrite = nullptr;
- if (context_->GetPackageId() > kAppPackageId &&
+ // Pre-O, the platform treats negative resource IDs [those with a package ID of 0x80
+ // or higher] as invalid. In order to work around this limitation, we allow the use
+ // of traditionally reserved resource IDs [those between 0x02 and 0x7E]. Allow the
+ // definition of what a valid "split" package ID is to account for this.
+ const bool isSplitPackage = (options_.allow_reserved_package_id &&
+ context_->GetPackageId() != kAppPackageId &&
+ context_->GetPackageId() != kFrameworkPackageId)
+ || (!options_.allow_reserved_package_id && context_->GetPackageId() > kAppPackageId);
+ if (isSplitPackage &&
included_feature_base_ == make_value(context_->GetCompilationPackage())) {
// The base APK is included, and this is a feature split. If the base package is
// the same as this package, then we are building an old style Android Instant Apps feature
@@ -2150,6 +2164,10 @@
"Generates a text file containing the resource symbols of the R class in\n"
"the specified folder.",
&options.generate_text_symbols_path)
+ .OptionalSwitch("--allow-reserved-package-id",
+ "Allows the use of a reserved package ID. This should on be used for\n"
+ "packages with a pre-O min-sdk\n",
+ &options.allow_reserved_package_id)
.OptionalSwitch("--auto-add-overlay",
"Allows the addition of new resources in overlays without\n"
"<add-resource> tags.",
@@ -2249,7 +2267,9 @@
}
const uint32_t package_id_int = maybe_package_id_int.value();
- if (package_id_int < kAppPackageId || package_id_int > std::numeric_limits<uint8_t>::max()) {
+ if (package_id_int > std::numeric_limits<uint8_t>::max()
+ || package_id_int == kFrameworkPackageId
+ || (!options.allow_reserved_package_id && package_id_int < kAppPackageId)) {
context.GetDiagnostics()->Error(
DiagMessage() << StringPrintf(
"invalid package ID 0x%02x. Must be in the range 0x7f-0xff.", package_id_int));
diff --git a/tools/aapt2/link/ReferenceLinker.cpp b/tools/aapt2/link/ReferenceLinker.cpp
index 9aaaa69..3a5d585 100644
--- a/tools/aapt2/link/ReferenceLinker.cpp
+++ b/tools/aapt2/link/ReferenceLinker.cpp
@@ -340,6 +340,7 @@
// against libraries without assigned IDs.
// Ex: Linking against own resources when building a static library.
reference->id = s->id;
+ reference->is_dynamic = s->is_dynamic;
return true;
}
diff --git a/tools/aapt2/process/SymbolTable.cpp b/tools/aapt2/process/SymbolTable.cpp
index 2e97a2f..fc4c9b5 100644
--- a/tools/aapt2/process/SymbolTable.cpp
+++ b/tools/aapt2/process/SymbolTable.cpp
@@ -227,6 +227,10 @@
return package_map;
}
+bool AssetManagerSymbolSource::IsPackageDynamic(uint32_t packageId) const {
+ return assets_.getResources(false).isPackageDynamic(packageId);
+}
+
static std::unique_ptr<SymbolTable::Symbol> LookupAttributeInTable(
const android::ResTable& table, ResourceId id) {
// Try as a bag.
@@ -330,6 +334,7 @@
} else {
s = util::make_unique<SymbolTable::Symbol>();
s->id = res_id;
+ s->is_dynamic = table.isResourceDynamic(res_id.id);
}
if (s) {
@@ -354,7 +359,6 @@
// Exit early and avoid the error logs from AssetManager.
return {};
}
-
const android::ResTable& table = assets_.getResources(false);
Maybe<ResourceName> maybe_name = GetResourceName(table, id);
if (!maybe_name) {
@@ -370,6 +374,7 @@
} else {
s = util::make_unique<SymbolTable::Symbol>();
s->id = id;
+ s->is_dynamic = table.isResourceDynamic(id.id);
}
if (s) {
diff --git a/tools/aapt2/process/SymbolTable.h b/tools/aapt2/process/SymbolTable.h
index b676efb..51a2e37 100644
--- a/tools/aapt2/process/SymbolTable.h
+++ b/tools/aapt2/process/SymbolTable.h
@@ -68,6 +68,7 @@
Maybe<ResourceId> id;
std::shared_ptr<Attribute> attribute;
bool is_public = false;
+ bool is_dynamic = false;
};
SymbolTable(NameMangler* mangler);
@@ -192,6 +193,7 @@
bool AddAssetPath(const android::StringPiece& path);
std::map<size_t, std::string> GetAssignedPackageIds() const;
+ bool IsPackageDynamic(uint32_t packageId) const;
std::unique_ptr<SymbolTable::Symbol> FindByName(
const ResourceName& name) override;