Merge "AdaptiveIconDrawable should not update layer bounds when bound is empty" into oc-dr1-dev
diff --git a/Android.mk b/Android.mk
index 933ac62..55ea69a 100644
--- a/Android.mk
+++ b/Android.mk
@@ -292,6 +292,7 @@
core/java/android/service/euicc/IGetEidCallback.aidl \
core/java/android/service/euicc/IGetEuiccInfoCallback.aidl \
core/java/android/service/euicc/IGetEuiccProfileInfoListCallback.aidl \
+ core/java/android/service/euicc/IRetainSubscriptionsForFactoryResetCallback.aidl \
core/java/android/service/euicc/ISwitchToSubscriptionCallback.aidl \
core/java/android/service/euicc/IUpdateSubscriptionNicknameCallback.aidl \
core/java/android/service/gatekeeper/IGateKeeperService.aidl \
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index bc6e9cd..0ff3215 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -6416,17 +6416,7 @@
*/
@Deprecated
public boolean requestVisibleBehind(boolean visible) {
- if (!mResumed) {
- // Do not permit paused or stopped activities to do this.
- visible = false;
- }
- try {
- mVisibleBehind = ActivityManager.getService()
- .requestVisibleBehind(mToken, visible) && visible;
- } catch (RemoteException e) {
- mVisibleBehind = false;
- }
- return mVisibleBehind;
+ return false;
}
/**
diff --git a/core/java/android/content/pm/permission/IRuntimePermissionPresenter.aidl b/core/java/android/content/pm/permission/IRuntimePermissionPresenter.aidl
index 3c3b84d..9490e27 100644
--- a/core/java/android/content/pm/permission/IRuntimePermissionPresenter.aidl
+++ b/core/java/android/content/pm/permission/IRuntimePermissionPresenter.aidl
@@ -25,4 +25,5 @@
*/
oneway interface IRuntimePermissionPresenter {
void getAppPermissions(String packageName, in RemoteCallback callback);
+ void revokeRuntimePermission(String packageName, String permissionName);
}
diff --git a/core/java/android/content/pm/permission/RuntimePermissionPresenter.java b/core/java/android/content/pm/permission/RuntimePermissionPresenter.java
index 6d55d2f..02d0a6d 100644
--- a/core/java/android/content/pm/permission/RuntimePermissionPresenter.java
+++ b/core/java/android/content/pm/permission/RuntimePermissionPresenter.java
@@ -22,7 +22,6 @@
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
-import android.content.pm.ApplicationInfo;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
@@ -31,6 +30,7 @@
import android.os.RemoteException;
import android.permissionpresenterservice.RuntimePermissionPresenterService;
import android.util.Log;
+
import com.android.internal.annotations.GuardedBy;
import com.android.internal.os.SomeArgs;
@@ -118,6 +118,22 @@
mRemoteService.processMessage(message);
}
+ /**
+ * Revoke the permission {@code permissionName} for app {@code packageName}
+ *
+ * @param packageName The package for which to revoke
+ * @param permissionName The permission to revoke
+ */
+ public void revokeRuntimePermission(String packageName, String permissionName) {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = packageName;
+ args.arg2 = permissionName;
+
+ Message message = mRemoteService.obtainMessage(
+ RemoteService.MSG_REVOKE_APP_PERMISSIONS, args);
+ mRemoteService.processMessage(message);
+ }
+
private static final class RemoteService
extends Handler implements ServiceConnection {
private static final long UNBIND_TIMEOUT_MILLIS = 10000;
@@ -125,6 +141,7 @@
public static final int MSG_GET_APP_PERMISSIONS = 1;
public static final int MSG_GET_APPS_USING_PERMISSIONS = 2;
public static final int MSG_UNBIND = 3;
+ public static final int MSG_REVOKE_APP_PERMISSIONS = 4;
private final Object mLock = new Object();
@@ -231,6 +248,25 @@
mRemoteInstance = null;
}
} break;
+
+ case MSG_REVOKE_APP_PERMISSIONS: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ final String packageName = (String) args.arg1;
+ final String permissionName = (String) args.arg2;
+ args.recycle();
+ final IRuntimePermissionPresenter remoteInstance;
+ synchronized (mLock) {
+ remoteInstance = mRemoteInstance;
+ }
+ if (remoteInstance == null) {
+ return;
+ }
+ try {
+ remoteInstance.revokeRuntimePermission(packageName, permissionName);
+ } catch (RemoteException re) {
+ Log.e(TAG, "Error getting app permissions", re);
+ }
+ } break;
}
synchronized (mLock) {
diff --git a/core/java/android/permissionpresenterservice/RuntimePermissionPresenterService.java b/core/java/android/permissionpresenterservice/RuntimePermissionPresenterService.java
index 344d947..2931627f 100644
--- a/core/java/android/permissionpresenterservice/RuntimePermissionPresenterService.java
+++ b/core/java/android/permissionpresenterservice/RuntimePermissionPresenterService.java
@@ -20,7 +20,6 @@
import android.app.Service;
import android.content.Context;
import android.content.Intent;
-import android.content.pm.ApplicationInfo;
import android.content.pm.permission.IRuntimePermissionPresenter;
import android.content.pm.permission.RuntimePermissionPresentationInfo;
import android.content.pm.permission.RuntimePermissionPresenter;
@@ -30,6 +29,7 @@
import android.os.Looper;
import android.os.Message;
import android.os.RemoteCallback;
+
import com.android.internal.os.SomeArgs;
import java.util.List;
@@ -72,6 +72,16 @@
*/
public abstract List<RuntimePermissionPresentationInfo> onGetAppPermissions(String packageName);
+ /**
+ * Revoke the permission {@code permissionName} for app {@code packageName}
+ *
+ * @param packageName The package for which to revoke
+ * @param permissionName The permission to revoke
+ *
+ * @hide
+ */
+ public abstract void onRevokeRuntimePermission(String packageName, String permissionName);
+
@Override
public final IBinder onBind(Intent intent) {
return new IRuntimePermissionPresenter.Stub() {
@@ -83,12 +93,22 @@
mHandler.obtainMessage(MyHandler.MSG_GET_APP_PERMISSIONS,
args).sendToTarget();
}
+
+ @Override
+ public void revokeRuntimePermission(String packageName, String permissionName) {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = packageName;
+ args.arg2 = permissionName;
+ mHandler.obtainMessage(MyHandler.MSG_REVOKE_APP_PERMISSION,
+ args).sendToTarget();
+ }
};
}
private final class MyHandler extends Handler {
public static final int MSG_GET_APP_PERMISSIONS = 1;
public static final int MSG_GET_APPS_USING_PERMISSIONS = 2;
+ public static final int MSG_REVOKE_APP_PERMISSION = 3;
public MyHandler(Looper looper) {
super(looper, null, false);
@@ -113,6 +133,14 @@
callback.sendResult(null);
}
} break;
+ case MSG_REVOKE_APP_PERMISSION: {
+ SomeArgs args = (SomeArgs) msg.obj;
+ String packageName = (String) args.arg1;
+ String permissionName = (String) args.arg2;
+ args.recycle();
+
+ onRevokeRuntimePermission(packageName, permissionName);
+ } break;
}
}
}
diff --git a/core/java/android/service/euicc/EuiccService.java b/core/java/android/service/euicc/EuiccService.java
index 26f8528..0c2e4b7 100644
--- a/core/java/android/service/euicc/EuiccService.java
+++ b/core/java/android/service/euicc/EuiccService.java
@@ -317,6 +317,21 @@
public abstract int onEraseSubscriptions(int slotId);
/**
+ * Ensure that subscriptions will be retained on the next factory reset.
+ *
+ * <p>Called directly before a factory reset. Assumes that a normal factory reset will lead to
+ * profiles being erased on first boot (to cover fastboot/recovery wipes), so the implementation
+ * should persist some bit that will remain accessible after the factory reset to bypass this
+ * flow when this method is called.
+ *
+ * @param slotId ID of the SIM slot to use for the operation. This is currently not populated
+ * but is here to future-proof the APIs.
+ * @return the result of the operation. May be one of the predefined {@code RESULT_} constants
+ * or any implementation-specific code starting with {@link #RESULT_FIRST_USER}.
+ */
+ public abstract int onRetainSubscriptionsForFactoryReset(int slotId);
+
+ /**
* Wrapper around IEuiccService that forwards calls to implementations of {@link EuiccService}.
*/
private class IEuiccServiceWrapper extends IEuiccService.Stub {
@@ -488,5 +503,21 @@
}
});
}
+
+ @Override
+ public void retainSubscriptionsForFactoryReset(int slotId,
+ IRetainSubscriptionsForFactoryResetCallback callback) {
+ mExecutor.execute(new Runnable() {
+ @Override
+ public void run() {
+ int result = EuiccService.this.onRetainSubscriptionsForFactoryReset(slotId);
+ try {
+ callback.onComplete(result);
+ } catch (RemoteException e) {
+ // Can't communicate with the phone process; ignore.
+ }
+ }
+ });
+ }
}
}
diff --git a/core/java/android/service/euicc/IEuiccService.aidl b/core/java/android/service/euicc/IEuiccService.aidl
index 18ea915..e10dd8c 100644
--- a/core/java/android/service/euicc/IEuiccService.aidl
+++ b/core/java/android/service/euicc/IEuiccService.aidl
@@ -24,6 +24,7 @@
import android.service.euicc.IGetEidCallback;
import android.service.euicc.IGetEuiccInfoCallback;
import android.service.euicc.IGetEuiccProfileInfoListCallback;
+import android.service.euicc.IRetainSubscriptionsForFactoryResetCallback;
import android.service.euicc.ISwitchToSubscriptionCallback;
import android.service.euicc.IUpdateSubscriptionNicknameCallback;
import android.telephony.euicc.DownloadableSubscription;
@@ -46,4 +47,6 @@
void updateSubscriptionNickname(int slotId, String iccid, String nickname,
in IUpdateSubscriptionNicknameCallback callback);
void eraseSubscriptions(int slotId, in IEraseSubscriptionsCallback callback);
+ void retainSubscriptionsForFactoryReset(
+ int slotId, in IRetainSubscriptionsForFactoryResetCallback callback);
}
\ No newline at end of file
diff --git a/core/java/android/service/euicc/IRetainSubscriptionsForFactoryResetCallback.aidl b/core/java/android/service/euicc/IRetainSubscriptionsForFactoryResetCallback.aidl
new file mode 100644
index 0000000..1276830
--- /dev/null
+++ b/core/java/android/service/euicc/IRetainSubscriptionsForFactoryResetCallback.aidl
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2017 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.service.euicc;
+
+/** @hide */
+oneway interface IRetainSubscriptionsForFactoryResetCallback {
+ void onComplete(int result);
+}
\ No newline at end of file
diff --git a/core/java/android/util/Log.java b/core/java/android/util/Log.java
index 951aa8d..8691136 100644
--- a/core/java/android/util/Log.java
+++ b/core/java/android/util/Log.java
@@ -30,8 +30,9 @@
/**
* API for sending log output.
*
- * <p>Generally, use the Log.v() Log.d() Log.i() Log.w() and Log.e()
- * methods.
+ * <p>Generally, you should use the {@link #v Log.v()}, {@link #d Log.d()},
+ * {@link #i Log.i()}, {@link #w Log.w()}, and {@link #e Log.e()} methods to write logs.
+ * You can then <a href="{@docRoot}studio/debug/am-logcat.html">view the logs in logcat</a>.
*
* <p>The order in terms of verbosity, from least to most is
* ERROR, WARN, INFO, DEBUG, VERBOSE. Verbose should never be compiled
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index 8fe9100..544afd9 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -3103,12 +3103,29 @@
}
/**
+ * Check if Setup or Post-Setup update is completed on TV
+ * @return true if completed
+ */
+ private boolean isTvUserSetupComplete() {
+ boolean isTvSetupComplete = Settings.Secure.getInt(getContext().getContentResolver(),
+ Settings.Secure.USER_SETUP_COMPLETE, 0) != 0;
+ isTvSetupComplete &= Settings.Secure.getInt(getContext().getContentResolver(),
+ Settings.Secure.TV_USER_SETUP_COMPLETE, 0) != 0;
+ return isTvSetupComplete;
+ }
+
+ /**
* Helper method for adding launch-search to most applications. Opens the
* search window using default settings.
*
* @return true if search window opened
*/
private boolean launchDefaultSearch(KeyEvent event) {
+ if (getContext().getPackageManager().hasSystemFeature(PackageManager.FEATURE_LEANBACK)
+ && !isTvUserSetupComplete()) {
+ // If we are in Setup or Post-Setup update mode on TV, consume the search key
+ return false;
+ }
boolean result;
final Callback cb = getCallback();
if (cb == null || isDestroyed()) {
diff --git a/core/java/com/android/internal/policy/PipSnapAlgorithm.java b/core/java/com/android/internal/policy/PipSnapAlgorithm.java
index 95d714f..749d00c1 100644
--- a/core/java/com/android/internal/policy/PipSnapAlgorithm.java
+++ b/core/java/com/android/internal/policy/PipSnapAlgorithm.java
@@ -49,9 +49,6 @@
// Allows snapping on the long edge in each orientation and magnets towards corners
private static final int SNAP_MODE_LONG_EDGE_MAGNET_CORNERS = 4;
- // The friction multiplier to control how slippery the PIP is when flung
- private static final float SCROLL_FRICTION_MULTIPLIER = 8f;
-
// Threshold to magnet to a corner
private static final float CORNER_MAGNET_THRESHOLD = 0.3f;
@@ -64,8 +61,8 @@
private final float mDefaultSizePercent;
private final float mMinAspectRatioForMinSize;
private final float mMaxAspectRatioForMinSize;
+ private final int mFlingDeceleration;
- private Scroller mScroller;
private int mOrientation = Configuration.ORIENTATION_UNDEFINED;
private final int mMinimizedVisibleSize;
@@ -81,6 +78,8 @@
mMaxAspectRatioForMinSize = res.getFloat(
com.android.internal.R.dimen.config_pictureInPictureAspectRatioLimitForMinSize);
mMinAspectRatioForMinSize = 1f / mMaxAspectRatioForMinSize;
+ mFlingDeceleration = mContext.getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.pip_fling_deceleration);
onConfigurationChanged();
}
@@ -107,20 +106,97 @@
* those for the given {@param stackBounds}.
*/
public Rect findClosestSnapBounds(Rect movementBounds, Rect stackBounds, float velocityX,
- float velocityY) {
- final Rect finalStackBounds = new Rect(stackBounds);
- if (mScroller == null) {
- final ViewConfiguration viewConfig = ViewConfiguration.get(mContext);
- mScroller = new Scroller(mContext);
- mScroller.setFriction(viewConfig.getScrollFriction() * SCROLL_FRICTION_MULTIPLIER);
+ float velocityY, Point dragStartPosition) {
+ final Rect intersectStackBounds = new Rect(stackBounds);
+ final Point intersect = getEdgeIntersect(stackBounds, movementBounds, velocityX, velocityY,
+ dragStartPosition);
+ intersectStackBounds.offsetTo(intersect.x, intersect.y);
+ return findClosestSnapBounds(movementBounds, intersectStackBounds);
+ }
+
+ /**
+ * @return The point along the {@param movementBounds} that the PIP would intersect with based
+ * on the provided {@param velX}, {@param velY} along with the position of the PIP when
+ * the gesture started, {@param dragStartPosition}.
+ */
+ public Point getEdgeIntersect(Rect stackBounds, Rect movementBounds, float velX, float velY,
+ Point dragStartPosition) {
+ final boolean isLandscape = mOrientation == Configuration.ORIENTATION_LANDSCAPE;
+ final int x = stackBounds.left;
+ final int y = stackBounds.top;
+
+ // Find the line of movement the PIP is on. Line defined by: y = slope * x + yIntercept
+ final float slope = velY / velX; // slope = rise / run
+ final float yIntercept = y - slope * x; // rearrange line equation for yIntercept
+ // The PIP can have two intercept points:
+ // 1) Where the line intersects with one of the edges of the screen (vertical line)
+ Point vertPoint = new Point();
+ // 2) Where the line intersects with the top or bottom of the screen (horizontal line)
+ Point horizPoint = new Point();
+
+ // Find the vertical line intersection, x will be one of the edges
+ vertPoint.x = velX > 0 ? movementBounds.right : movementBounds.left;
+ // Sub in x in our line equation to determine y position
+ vertPoint.y = findY(slope, yIntercept, vertPoint.x);
+
+ // Find the horizontal line intersection, y will be the top or bottom of the screen
+ horizPoint.y = velY > 0 ? movementBounds.bottom : movementBounds.top;
+ // Sub in y in our line equation to determine x position
+ horizPoint.x = findX(slope, yIntercept, horizPoint.y);
+
+ // Now pick one of these points -- first determine if we're flinging along the current edge.
+ // Only fling along current edge if it's a direction with space for the PIP to move to
+ int maxDistance;
+ if (isLandscape) {
+ maxDistance = velX > 0
+ ? movementBounds.right - stackBounds.left
+ : stackBounds.left - movementBounds.left;
+ } else {
+ maxDistance = velY > 0
+ ? movementBounds.bottom - stackBounds.top
+ : stackBounds.top - movementBounds.top;
}
- mScroller.fling(stackBounds.left, stackBounds.top,
- (int) velocityX, (int) velocityY,
- movementBounds.left, movementBounds.right,
- movementBounds.top, movementBounds.bottom);
- finalStackBounds.offsetTo(mScroller.getFinalX(), mScroller.getFinalY());
- mScroller.abortAnimation();
- return findClosestSnapBounds(movementBounds, finalStackBounds);
+ if (maxDistance > 0) {
+ // Only fling along the current edge if the start and end point are on the same side
+ final int startPoint = isLandscape ? dragStartPosition.y : dragStartPosition.x;
+ final int endPoint = isLandscape ? horizPoint.y : horizPoint.x;
+ final int center = movementBounds.centerX();
+ if ((startPoint < center && endPoint < center)
+ || (startPoint > center && endPoint > center)) {
+ // We are flinging along the current edge, figure out how far it should travel
+ // based on velocity and assumed deceleration.
+ int distance = (int) (0 - Math.pow(isLandscape ? velX : velY, 2))
+ / (2 * mFlingDeceleration);
+ distance = Math.min(distance, maxDistance);
+ // Adjust the point for the distance
+ if (isLandscape) {
+ horizPoint.x = stackBounds.left + (velX > 0 ? distance : -distance);
+ } else {
+ horizPoint.y = stackBounds.top + (velY > 0 ? distance : -distance);
+ }
+ return horizPoint;
+ }
+ }
+ // If we're not flinging along the current edge, find the closest point instead.
+ final double distanceVert = Math.hypot(vertPoint.x - x, vertPoint.y - y);
+ final double distanceHoriz = Math.hypot(horizPoint.x - x, horizPoint.y - y);
+ // Ensure that we're actually going somewhere
+ if (distanceVert == 0) {
+ return horizPoint;
+ }
+ if (distanceHoriz == 0) {
+ return vertPoint;
+ }
+ // Otherwise use the closest point
+ return Math.abs(distanceVert) > Math.abs(distanceHoriz) ? horizPoint : vertPoint;
+ }
+
+ private int findY(float slope, float yIntercept, float x) {
+ return (int) ((slope * x) + yIntercept);
+ }
+
+ private int findX(float slope, float yIntercept, float y) {
+ return (int) ((y - yIntercept) / slope);
}
/**
diff --git a/core/jni/android_os_HwBinder.cpp b/core/jni/android_os_HwBinder.cpp
index cdd3c09..6c6fa66 100644
--- a/core/jni/android_os_HwBinder.cpp
+++ b/core/jni/android_os_HwBinder.cpp
@@ -370,7 +370,7 @@
if (transport != IServiceManager::Transport::HWBINDER && !vintfLegacy) {
LOG(ERROR) << "service " << ifaceName << " declares transport method "
<< toString(transport) << " but framework expects hwbinder.";
- signalExceptionForError(env, UNKNOWN_ERROR, true /* canThrowRemoteException */);
+ signalExceptionForError(env, NAME_NOT_FOUND, true /* canThrowRemoteException */);
return NULL;
}
diff --git a/core/res/res/anim/task_close_enter.xml b/core/res/res/anim/task_close_enter.xml
index b07f470..bea0ee5 100644
--- a/core/res/res/anim/task_close_enter.xml
+++ b/core/res/res/anim/task_close_enter.xml
@@ -18,7 +18,7 @@
-->
<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:background="#ff000000" android:shareInterpolator="false" android:zAdjustment="normal">
+ android:shareInterpolator="false" android:zAdjustment="normal">
<alpha android:fromAlpha="0.6" android:toAlpha="1.0"
android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
diff --git a/core/res/res/anim/task_close_exit.xml b/core/res/res/anim/task_close_exit.xml
index d23c74f..b6a0807 100644
--- a/core/res/res/anim/task_close_exit.xml
+++ b/core/res/res/anim/task_close_exit.xml
@@ -18,7 +18,7 @@
-->
<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:background="#ff000000" android:shareInterpolator="false" android:zAdjustment="top">
+ android:shareInterpolator="false" android:zAdjustment="top">
<alpha android:fromAlpha="1.0" android:toAlpha="0"
android:fillEnabled="true" android:fillBefore="true" android:fillAfter="true"
diff --git a/core/res/res/layout/notification_template_right_icon.xml b/core/res/res/layout/notification_template_right_icon.xml
index aa4e05b..fbf7538 100644
--- a/core/res/res/layout/notification_template_right_icon.xml
+++ b/core/res/res/layout/notification_template_right_icon.xml
@@ -26,8 +26,9 @@
android:layout_gravity="top|end"
android:layout_marginTop="36dp"
android:layout_marginEnd="@dimen/notification_content_margin_end"
- android:scaleType="centerCrop"/>
- <ImageView android:id="@+id/reply_icon_action"
+ android:scaleType="centerCrop"
+ android:importantForAccessibility="no" />
+ <ImageButton android:id="@+id/reply_icon_action"
android:layout_width="16dp"
android:layout_height="16dp"
android:layout_gravity="top|end"
@@ -36,6 +37,7 @@
android:background="@drawable/notification_reply_background"
android:src="@drawable/ic_reply_notification"
android:scaleType="center"
+ android:contentDescription="@string/notification_reply_button_accessibility"
visiblity="gone"/>
</FrameLayout>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index d47bde7..69bb0b9 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2983,4 +2983,7 @@
<!-- Show area update info settings in CellBroadcastReceiver and information in SIM status in Settings app -->
<bool name="config_showAreaUpdateInfoSettings">false</bool>
+
+ <!-- Enable the RingtonePickerActivity in 'com.android.providers.media'. -->
+ <bool name="config_defaultRingtonePickerEnabled">true</bool>
</resources>
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index 5e2334d..fa33d56 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -67,6 +67,9 @@
<!-- The amount to leave on-screen when the PIP is minimized. -->
<dimen name="pip_minimized_visible_size">48dp</dimen>
+ <!-- The the PIP decelerates at while moving from a fling. -->
+ <dimen name="pip_fling_deceleration">-3000dp</dimen>
+
<!-- Min width for a tablet device -->
<dimen name="min_xlarge_screen_width">800dp</dimen>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index ce701a7..bd8e14ea 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -4713,6 +4713,9 @@
<!-- Primary ETWS (Earthquake and Tsunami Warning System) default message for test -->
<string name="etws_primary_default_message_test">Emergency messages test</string>
+ <!-- Content description for the reply button in the notification area [CHAR LIMIT=NONE]-->
+ <string name="notification_reply_button_accessibility">Reply</string>
+
<!-- Primary ETWS (Earthquake and Tsunami Warning System) default message for others -->
<string name="etws_primary_default_message_others"></string>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 1614efb..99807f9 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1583,6 +1583,7 @@
<java-symbol type="dimen" name="docked_stack_divider_insets" />
<java-symbol type="dimen" name="docked_stack_minimize_thickness" />
<java-symbol type="dimen" name="pip_minimized_visible_size" />
+ <java-symbol type="dimen" name="pip_fling_deceleration" />
<java-symbol type="integer" name="config_dockedStackDividerSnapMode" />
<java-symbol type="integer" name="config_pictureInPictureSnapMode" />
<java-symbol type="fraction" name="docked_stack_divider_fixed_ratio" />
diff --git a/libs/hwui/GlopBuilder.cpp b/libs/hwui/GlopBuilder.cpp
index 2e9a6e8..c19c1a1 100644
--- a/libs/hwui/GlopBuilder.cpp
+++ b/libs/hwui/GlopBuilder.cpp
@@ -457,11 +457,13 @@
return *this;
}
-GlopBuilder& GlopBuilder::setFillExternalTexture(Texture& texture, Matrix4& textureTransform) {
+GlopBuilder& GlopBuilder::setFillExternalTexture(Texture& texture, Matrix4& textureTransform,
+ bool requiresFilter) {
TRIGGER_STAGE(kFillStage);
REQUIRE_STAGES(kMeshStage | kRoundRectClipStage);
- mOutGlop->fill.texture = { &texture, GL_LINEAR, GL_CLAMP_TO_EDGE, &textureTransform };
+ GLenum filter = requiresFilter ? GL_LINEAR : GL_NEAREST;
+ mOutGlop->fill.texture = { &texture, filter, GL_CLAMP_TO_EDGE, &textureTransform };
setFill(SK_ColorWHITE, 1.0f, SkBlendMode::kSrc, Blend::ModeOrderSwap::NoSwap,
nullptr, nullptr);
diff --git a/libs/hwui/GlopBuilder.h b/libs/hwui/GlopBuilder.h
index 87b1568..6d11da1 100644
--- a/libs/hwui/GlopBuilder.h
+++ b/libs/hwui/GlopBuilder.h
@@ -75,7 +75,8 @@
GlopBuilder& setFillTextureLayer(GlLayer& layer, float alpha);
// TODO: setFillLayer normally forces its own wrap & filter mode,
// which isn't always correct.
- GlopBuilder& setFillExternalTexture(Texture& texture, Matrix4& textureTransform);
+ GlopBuilder& setFillExternalTexture(Texture& texture, Matrix4& textureTransform,
+ bool requiresFilter);
GlopBuilder& setTransform(const Matrix4& canvas, const int transformFlags);
diff --git a/libs/hwui/OpenGLReadback.cpp b/libs/hwui/OpenGLReadback.cpp
index b073070b..19d5d9d 100644
--- a/libs/hwui/OpenGLReadback.cpp
+++ b/libs/hwui/OpenGLReadback.cpp
@@ -199,6 +199,7 @@
GL_TEXTURE_2D, texture, 0);
{
+ bool requiresFilter;
// Draw & readback
renderState.setViewport(destWidth, destHeight);
renderState.scissor().setEnabled(false);
@@ -216,12 +217,17 @@
croppedTexTransform.scale(srcRect.getWidth() / sourceTexture.width(),
srcRect.getHeight() / sourceTexture.height(), 1);
croppedTexTransform.multiply(sFlipV);
+ requiresFilter = srcRect.getWidth() != (float) destWidth
+ || srcRect.getHeight() != (float) destHeight;
+ } else {
+ requiresFilter = sourceTexture.width() != (uint32_t) destWidth
+ || sourceTexture.height() != (uint32_t) destHeight;
}
Glop glop;
GlopBuilder(renderState, caches, &glop)
.setRoundRectClipState(nullptr)
.setMeshTexturedUnitQuad(nullptr)
- .setFillExternalTexture(sourceTexture, croppedTexTransform)
+ .setFillExternalTexture(sourceTexture, croppedTexTransform, requiresFilter)
.setTransform(Matrix4::identity(), TransformFlags::None)
.setModelViewMapUnitToRect(Rect(destWidth, destHeight))
.build();
diff --git a/location/java/android/location/GnssStatus.java b/location/java/android/location/GnssStatus.java
index 912551f..b2903c4 100644
--- a/location/java/android/location/GnssStatus.java
+++ b/location/java/android/location/GnssStatus.java
@@ -97,13 +97,12 @@
CONSTELLATION_QZSS, CONSTELLATION_BEIDOU, CONSTELLATION_GALILEO})
public @interface ConstellationType {}
- /* These package private values are modified by the LocationManager class */
- /* package */ int[] mSvidWithFlags;
- /* package */ float[] mCn0DbHz;
- /* package */ float[] mElevations;
- /* package */ float[] mAzimuths;
- /* package */ int mSvCount;
- /* package */ float[] mCarrierFrequencies;
+ final int[] mSvidWithFlags;
+ final float[] mCn0DbHz;
+ final float[] mElevations;
+ final float[] mAzimuths;
+ final int mSvCount;
+ final float[] mCarrierFrequencies;
GnssStatus(int svCount, int[] svidWithFlags, float[] cn0s, float[] elevations,
float[] azimuths, float[] carrierFrequencies) {
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index 26ac2a2..968f596 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -74,7 +74,8 @@
new HashMap<>();
private final HashMap<OnNmeaMessageListener, GnssStatusListenerTransport> mGnssNmeaListeners =
new HashMap<>();
- private GnssStatus mGnssStatus;
+ // volatile + GnssStatus final-fields pattern to avoid a partially published object
+ private volatile GnssStatus mGnssStatus;
private int mTimeToFirstFix;
/**
@@ -1563,7 +1564,7 @@
float[] cn0s, float[] elevations, float[] azimuths, float[] carrierFreqs) {
if (mGnssCallback != null) {
mGnssStatus = new GnssStatus(svCount, prnWithFlags, cn0s, elevations, azimuths,
- carrierFreqs);
+ carrierFreqs);
Message msg = Message.obtain();
msg.what = GpsStatus.GPS_EVENT_SATELLITE_STATUS;
@@ -1949,7 +1950,7 @@
status = new GpsStatus();
}
// When mGnssStatus is null, that means that this method is called outside
- // onGpsStatusChanged(). Return an empty status to maintain backwards compatibility.
+ // onGpsStatusChanged(). Return an empty status to maintain backwards compatibility.
if (mGnssStatus != null) {
status.setStatus(mGnssStatus, mTimeToFirstFix);
}
diff --git a/media/java/android/media/AudioManagerInternal.java b/media/java/android/media/AudioManagerInternal.java
index 0a1de33..2b5ac5e 100644
--- a/media/java/android/media/AudioManagerInternal.java
+++ b/media/java/android/media/AudioManagerInternal.java
@@ -59,4 +59,15 @@
int getRingerModeAffectedStreams(int streams);
}
+
+ /**
+ * Disable or restore the ability to play audio for a given UID.
+ * When a UID isn't meant to be tracked anymore (e.g. client died), re-enable audio for this UID
+ * to prevent disabling audio for future UIDs that would reuse the same value.
+ * This operation is asynchronous.
+ * @param disable when true, prevents playback of audio streams from the given uid. If false,
+ * restores the ability to play, or no-op if playback hadn't been disabled before.
+ * @param uid the client UID whose ability to play will be affected.
+ */
+ public abstract void disableAudioForUid(boolean disable, int uid);
}
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java
index 3b29a6c..1e262314 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/DeviceDiscoveryService.java
@@ -77,7 +77,7 @@
private BluetoothAdapter mBluetoothAdapter;
private WifiManager mWifiManager;
- private BluetoothLeScanner mBLEScanner;
+ @Nullable private BluetoothLeScanner mBLEScanner;
private ScanSettings mDefaultScanSettings = new ScanSettings.Builder().build();
private List<DeviceFilter<?>> mFilters;
@@ -185,7 +185,7 @@
mBluetoothAdapter.startDiscovery();
}
- if (shouldScan(mBLEFilters)) {
+ if (shouldScan(mBLEFilters) && mBLEScanner != null) {
mBLEScanCallback = new BLEScanCallback();
mBLEScanner.startScan(mBLEScanFilters, mDefaultScanSettings, mBLEScanCallback);
}
@@ -224,7 +224,7 @@
unregisterReceiver(mBluetoothBroadcastReceiver);
mBluetoothBroadcastReceiver = null;
}
- mBLEScanner.stopScan(mBLEScanCallback);
+ if (mBLEScanner != null) mBLEScanner.stopScan(mBLEScanCallback);
if (mWifiBroadcastReceiver != null) {
unregisterReceiver(mWifiBroadcastReceiver);
mWifiBroadcastReceiver = null;
diff --git a/packages/SettingsLib/src/com/android/settingslib/TronUtils.java b/packages/SettingsLib/src/com/android/settingslib/TronUtils.java
index bea6e8f..945cb57 100644
--- a/packages/SettingsLib/src/com/android/settingslib/TronUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/TronUtils.java
@@ -16,48 +16,18 @@
package com.android.settingslib;
import android.content.Context;
-import android.net.NetworkBadging;
import com.android.internal.logging.MetricsLogger;
+import com.android.settingslib.wifi.AccessPoint.Speed;
/** Utilites for Tron Logging. */
public final class TronUtils {
+ private static final String TAG = "TronUtils";
+
private TronUtils() {};
- public static void logWifiSettingsBadge(Context context, int badgeEnum) {
- logNetworkBadgeMetric(context, "settings_wifibadging", badgeEnum);
- }
-
- /**
- * Logs an occurrence of the given network badge to a Histogram.
- *
- * @param context Context
- * @param histogram the Tron histogram name to write to
- * @param badgeEnum the {@link NetworkBadging.Badging} badge value
- * @throws IllegalArgumentException if the given badge enum is not supported
- */
- private static void logNetworkBadgeMetric(
- Context context, String histogram, int badgeEnum)
- throws IllegalArgumentException {
- int bucket;
- switch (badgeEnum) {
- case NetworkBadging.BADGING_NONE:
- bucket = 0;
- break;
- case NetworkBadging.BADGING_SD:
- bucket = 1;
- break;
- case NetworkBadging.BADGING_HD:
- bucket = 2;
- break;
- case NetworkBadging.BADGING_4K:
- bucket = 3;
- break;
- default:
- throw new IllegalArgumentException("Unsupported badge enum: " + badgeEnum);
- }
-
- MetricsLogger.histogram(context, histogram, bucket);
+ public static void logWifiSettingsSpeed(Context context, @Speed int speedEnum) {
+ MetricsLogger.histogram(context, "settings_wifi_speed_labels", speedEnum);
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
index d45ed19..4a54451 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
@@ -16,6 +16,7 @@
package com.android.settingslib.wifi;
+import android.annotation.IntDef;
import android.annotation.Nullable;
import android.app.AppGlobals;
import android.content.Context;
@@ -53,6 +54,8 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.settingslib.R;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.concurrent.ConcurrentHashMap;
@@ -82,35 +85,30 @@
*/
public static final int HIGHER_FREQ_5GHZ = 5900;
- /**
- * Constant value representing an unlabeled / unscored network.
- */
- @VisibleForTesting
- static final int SPEED_NONE = 0;
-
- /**
- * Constant value representing a slow speed network connection.
- */
- @VisibleForTesting
- static final int SPEED_SLOW = 5;
-
- /**
- * Constant value representing a medium speed network connection.
- */
- @VisibleForTesting
- static final int SPEED_MEDIUM = 10;
-
- /**
- * Constant value representing a fast speed network connection.
- */
- @VisibleForTesting
- static final int SPEED_FAST = 20;
-
- /**
- * Constant value representing a very fast speed network connection.
- */
- @VisibleForTesting
- static final int SPEED_VERY_FAST = 30;
+ @IntDef({Speed.NONE, Speed.SLOW, Speed.MODERATE, Speed.FAST, Speed.VERY_FAST})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Speed {
+ /**
+ * Constant value representing an unlabeled / unscored network.
+ */
+ int NONE = 0;
+ /**
+ * Constant value representing a slow speed network connection.
+ */
+ int SLOW = 5;
+ /**
+ * Constant value representing a medium speed network connection.
+ */
+ int MODERATE = 10;
+ /**
+ * Constant value representing a fast speed network connection.
+ */
+ int FAST = 20;
+ /**
+ * Constant value representing a very fast speed network connection.
+ */
+ int VERY_FAST = 30;
+ }
/**
* Experimental: we should be able to show the user the list of BSSIDs and bands
@@ -177,7 +175,7 @@
private Object mTag;
private int mRankingScore = Integer.MIN_VALUE;
- private int mSpeed = AccessPoint.SPEED_NONE;
+ private int mSpeed = Speed.NONE;
private boolean mIsScoredNetworkMetered = false;
// used to co-relate internal vs returned accesspoint.
@@ -322,8 +320,15 @@
if (difference != 0) {
return difference;
}
+
// Sort by ssid.
- return getSsidStr().compareToIgnoreCase(other.getSsidStr());
+ difference = getSsidStr().compareToIgnoreCase(other.getSsidStr());
+ if (difference != 0) {
+ return difference;
+ }
+
+ // Do a case sensitive comparison to distinguish SSIDs that differ in case only
+ return getSsidStr().compareTo(other.getSsidStr());
}
@Override
@@ -368,7 +373,7 @@
if (mRankingScore != Integer.MIN_VALUE) {
builder.append(",rankingScore=").append(mRankingScore);
}
- if (mSpeed != SPEED_NONE) {
+ if (mSpeed != Speed.NONE) {
builder.append(",speed=").append(mSpeed);
}
builder.append(",metered=").append(isMetered());
@@ -399,7 +404,7 @@
private boolean updateScores(WifiNetworkScoreCache scoreCache) {
int oldSpeed = mSpeed;
int oldRankingScore = mRankingScore;
- mSpeed = SPEED_NONE;
+ mSpeed = Speed.NONE;
mRankingScore = Integer.MIN_VALUE;
for (ScanResult result : mScanResultCache.values()) {
@@ -675,7 +680,7 @@
// TODO(b/62354743): Standardize and international delimiter usage
final String concatenator = " / ";
- if (mSpeed != SPEED_NONE) {
+ if (mSpeed != Speed.NONE) {
summary.append(getSpeedLabel() + concatenator);
}
@@ -799,7 +804,7 @@
if (mRankingScore != Integer.MIN_VALUE) {
visibility.append(" rankingScore=").append(getRankingScore());
}
- if (mSpeed != SPEED_NONE) {
+ if (mSpeed != Speed.NONE) {
visibility.append(" speed=").append(getSpeedLabel());
}
visibility.append(String.format(" tx=%.1f,", mInfo.txSuccessRate));
@@ -1053,6 +1058,12 @@
final int oldLevel = getLevel();
if (info != null && isInfoForThisAccessPoint(config, info)) {
updated = (mInfo == null);
+ if (mConfig != config) {
+ // We do not set updated = true as we do not want to increase the amount of sorting
+ // and copying performed in WifiTracker at this time. If issues involving refresh
+ // are still seen, we will investigate further.
+ update(config); // Notifies the AccessPointListener of the change
+ }
if (mRssi != info.getRssi()) {
mRssi = info.getRssi();
updated = true;
@@ -1104,15 +1115,15 @@
@Nullable
String getSpeedLabel() {
switch (mSpeed) {
- case SPEED_VERY_FAST:
+ case Speed.VERY_FAST:
return mContext.getString(R.string.speed_label_very_fast);
- case SPEED_FAST:
+ case Speed.FAST:
return mContext.getString(R.string.speed_label_fast);
- case SPEED_MEDIUM:
+ case Speed.MODERATE:
return mContext.getString(R.string.speed_label_okay);
- case SPEED_SLOW:
+ case Speed.SLOW:
return mContext.getString(R.string.speed_label_slow);
- case SPEED_NONE:
+ case Speed.NONE:
default:
return null;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java
index e7525f3..e5977f3 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPointPreference.java
@@ -161,7 +161,7 @@
safeSetDefaultIcon();
return;
}
- TronUtils.logWifiSettingsBadge(context, mWifiSpeed);
+ TronUtils.logWifiSettingsSpeed(context, mWifiSpeed);
// TODO(b/62355275): Revert this to N code after deleting NetworkBadging API
Drawable drawable = NetworkBadging.getWifiIcon(
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
index 9083d90..85a453d 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/WifiTracker.java
@@ -91,7 +91,7 @@
private final boolean mIncludeScans;
private final boolean mIncludePasspoints;
@VisibleForTesting final MainHandler mMainHandler;
- private final WorkHandler mWorkHandler;
+ @VisibleForTesting final WorkHandler mWorkHandler;
private WifiTrackerNetworkCallback mNetworkCallback;
@@ -653,6 +653,7 @@
/* sticky broadcasts can call this when wifi is disabled */
if (!mWifiManager.isWifiEnabled()) {
mMainHandler.sendEmptyMessage(MainHandler.MSG_PAUSE_SCANNING);
+ clearAccessPointsAndConditionallyUpdate();
return;
}
@@ -696,6 +697,17 @@
}
}
+ private void clearAccessPointsAndConditionallyUpdate() {
+ synchronized (mLock) {
+ if (!mInternalAccessPoints.isEmpty()) {
+ mInternalAccessPoints.clear();
+ if (!mMainHandler.hasMessages(MainHandler.MSG_ACCESS_POINT_CHANGED)) {
+ mMainHandler.sendEmptyMessage(MainHandler.MSG_ACCESS_POINT_CHANGED);
+ }
+ }
+ }
+ }
+
/**
* Update all the internal access points rankingScores, badge and metering.
*
@@ -720,6 +732,9 @@
private void updateWifiState(int state) {
mWorkHandler.obtainMessage(WorkHandler.MSG_UPDATE_WIFI_STATE, state, 0).sendToTarget();
+ if (!mWifiManager.isWifiEnabled()) {
+ clearAccessPointsAndConditionallyUpdate();
+ }
}
public static List<AccessPoint> getCurrentAccessPoints(Context context, boolean includeSaved,
@@ -803,8 +818,15 @@
mListener.onWifiStateChanged(msg.arg1);
break;
case MSG_ACCESS_POINT_CHANGED:
- copyAndNotifyListeners(true /*notifyListeners*/);
- mListener.onAccessPointsChanged();
+ // Only notify listeners of changes if we have fresh scan results, otherwise the
+ // UI will be updated with stale results. We want to copy the APs regardless,
+ // for instances where forceUpdate was invoked by the caller.
+ if (mStaleScanResults) {
+ copyAndNotifyListeners(false /*notifyListeners*/);
+ } else {
+ copyAndNotifyListeners(true /*notifyListeners*/);
+ mListener.onAccessPointsChanged();
+ }
break;
case MSG_RESUME_SCANNING:
if (mScanner != null) {
@@ -828,7 +850,8 @@
}
}
- private final class WorkHandler extends Handler {
+ @VisibleForTesting
+ final class WorkHandler extends Handler {
private static final int MSG_UPDATE_ACCESS_POINTS = 0;
private static final int MSG_UPDATE_NETWORK_INFO = 1;
private static final int MSG_RESUME = 2;
diff --git a/packages/SettingsLib/tests/integ/AndroidManifest.xml b/packages/SettingsLib/tests/integ/AndroidManifest.xml
index 0d5ff2c..e8e0b41 100644
--- a/packages/SettingsLib/tests/integ/AndroidManifest.xml
+++ b/packages/SettingsLib/tests/integ/AndroidManifest.xml
@@ -21,6 +21,7 @@
<uses-permission android:name="android.permission.MANAGE_USERS" />
<uses-permission android:name="android.permission.MANAGE_NETWORK_POLICY"/>
<uses-permission android:name="android.permission.SET_TIME_ZONE" />
+ <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java
index 89328ee..5c64cff 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/AccessPointTest.java
@@ -20,6 +20,8 @@
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context;
@@ -169,6 +171,21 @@
}
@Test
+ public void testCompareTo_GivesSsidCasePrecendenceAfterAlphabetical() {
+
+ final String firstName = "aaAaaa";
+ final String secondName = "aaaaaa";
+ final String thirdName = "BBBBBB";
+
+ AccessPoint firstAp = new TestAccessPointBuilder(mContext).setSsid(firstName).build();
+ AccessPoint secondAp = new TestAccessPointBuilder(mContext).setSsid(secondName).build();
+ AccessPoint thirdAp = new TestAccessPointBuilder(mContext).setSsid(thirdName).build();
+
+ assertSortingWorks(firstAp, secondAp);
+ assertSortingWorks(secondAp, thirdAp);
+ }
+
+ @Test
public void testCompareTo_AllSortingRulesCombined() {
AccessPoint active = new TestAccessPointBuilder(mContext).setActive(true).build();
@@ -334,11 +351,11 @@
when(mockWifiNetworkScoreCache.getScoredNetwork(any(ScanResult.class)))
.thenReturn(buildScoredNetworkWithMockBadgeCurve());
- when(mockBadgeCurve.lookupScore(anyInt())).thenReturn((byte) AccessPoint.SPEED_VERY_FAST);
+ when(mockBadgeCurve.lookupScore(anyInt())).thenReturn((byte) AccessPoint.Speed.VERY_FAST);
ap.update(mockWifiNetworkScoreCache, true /* scoringUiEnabled */);
- assertThat(ap.getSpeed()).isEqualTo(AccessPoint.SPEED_VERY_FAST);
+ assertThat(ap.getSpeed()).isEqualTo(AccessPoint.Speed.VERY_FAST);
assertThat(ap.getSpeedLabel())
.isEqualTo(mContext.getString(R.string.speed_label_very_fast));
}
@@ -349,11 +366,11 @@
when(mockWifiNetworkScoreCache.getScoredNetwork(any(ScanResult.class)))
.thenReturn(buildScoredNetworkWithMockBadgeCurve());
- when(mockBadgeCurve.lookupScore(anyInt())).thenReturn((byte) AccessPoint.SPEED_FAST);
+ when(mockBadgeCurve.lookupScore(anyInt())).thenReturn((byte) AccessPoint.Speed.FAST);
ap.update(mockWifiNetworkScoreCache, true /* scoringUiEnabled */);
- assertThat(ap.getSpeed()).isEqualTo(AccessPoint.SPEED_FAST);
+ assertThat(ap.getSpeed()).isEqualTo(AccessPoint.Speed.FAST);
assertThat(ap.getSpeedLabel())
.isEqualTo(mContext.getString(R.string.speed_label_fast));
}
@@ -364,11 +381,11 @@
when(mockWifiNetworkScoreCache.getScoredNetwork(any(ScanResult.class)))
.thenReturn(buildScoredNetworkWithMockBadgeCurve());
- when(mockBadgeCurve.lookupScore(anyInt())).thenReturn((byte) AccessPoint.SPEED_MEDIUM);
+ when(mockBadgeCurve.lookupScore(anyInt())).thenReturn((byte) AccessPoint.Speed.MODERATE);
ap.update(mockWifiNetworkScoreCache, true /* scoringUiEnabled */);
- assertThat(ap.getSpeed()).isEqualTo(AccessPoint.SPEED_MEDIUM);
+ assertThat(ap.getSpeed()).isEqualTo(AccessPoint.Speed.MODERATE);
assertThat(ap.getSpeedLabel())
.isEqualTo(mContext.getString(R.string.speed_label_okay));
}
@@ -379,11 +396,11 @@
when(mockWifiNetworkScoreCache.getScoredNetwork(any(ScanResult.class)))
.thenReturn(buildScoredNetworkWithMockBadgeCurve());
- when(mockBadgeCurve.lookupScore(anyInt())).thenReturn((byte) AccessPoint.SPEED_SLOW);
+ when(mockBadgeCurve.lookupScore(anyInt())).thenReturn((byte) AccessPoint.Speed.SLOW);
ap.update(mockWifiNetworkScoreCache, true /* scoringUiEnabled */);
- assertThat(ap.getSpeed()).isEqualTo(AccessPoint.SPEED_SLOW);
+ assertThat(ap.getSpeed()).isEqualTo(AccessPoint.Speed.SLOW);
assertThat(ap.getSpeedLabel())
.isEqualTo(mContext.getString(R.string.speed_label_slow));
}
@@ -394,7 +411,7 @@
when(mockWifiNetworkScoreCache.getScoredNetwork(any(ScanResult.class)))
.thenReturn(buildScoredNetworkWithMockBadgeCurve());
- when(mockBadgeCurve.lookupScore(anyInt())).thenReturn((byte) AccessPoint.SPEED_VERY_FAST);
+ when(mockBadgeCurve.lookupScore(anyInt())).thenReturn((byte) AccessPoint.Speed.VERY_FAST);
ap.update(mockWifiNetworkScoreCache, true /* scoringUiEnabled */);
@@ -408,7 +425,7 @@
when(mockWifiNetworkScoreCache.getScoredNetwork(any(ScanResult.class)))
.thenReturn(buildScoredNetworkWithMockBadgeCurve());
- when(mockBadgeCurve.lookupScore(anyInt())).thenReturn((byte) AccessPoint.SPEED_VERY_FAST);
+ when(mockBadgeCurve.lookupScore(anyInt())).thenReturn((byte) AccessPoint.Speed.VERY_FAST);
ap.update(mockWifiNetworkScoreCache, true /* scoringUiEnabled */);
@@ -607,4 +624,36 @@
NetworkInfo newInfo = new NetworkInfo(networkInfo); // same values
assertThat(ap.update(config, wifiInfo, newInfo)).isFalse();
}
+
+ @Test
+ public void testUpdateWithConfigChangeOnly_returnsFalseButInvokesListener() {
+ int networkId = 123;
+ int rssi = -55;
+ WifiConfiguration config = new WifiConfiguration();
+ config.networkId = networkId;
+ config.numNoInternetAccessReports = 1;
+
+ WifiInfo wifiInfo = new WifiInfo();
+ wifiInfo.setNetworkId(networkId);
+ wifiInfo.setRssi(rssi);
+
+ NetworkInfo networkInfo =
+ new NetworkInfo(ConnectivityManager.TYPE_WIFI, 0 /* subtype */, "WIFI", "");
+ networkInfo.setDetailedState(NetworkInfo.DetailedState.CONNECTED, "", "");
+
+ AccessPoint ap = new TestAccessPointBuilder(mContext)
+ .setNetworkInfo(networkInfo)
+ .setNetworkId(networkId)
+ .setRssi(rssi)
+ .setWifiInfo(wifiInfo)
+ .build();
+
+ AccessPoint.AccessPointListener mockListener = mock(AccessPoint.AccessPointListener.class);
+ ap.setListener(mockListener);
+ WifiConfiguration newConfig = new WifiConfiguration(config);
+ config.validatedInternetAccess = true;
+
+ assertThat(ap.update(newConfig, wifiInfo, networkInfo)).isFalse();
+ verify(mockListener).onAccessPointChanged(ap);
+ }
}
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
index 340ef01..073da7e 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/wifi/WifiTrackerTest.java
@@ -94,7 +94,7 @@
new NetworkKey(new WifiKey('"' + SSID_1 + '"', BSSID_1));
private static final int RSSI_1 = -30;
private static final byte SCORE_1 = 10;
- private static final int BADGE_1 = AccessPoint.SPEED_MEDIUM;
+ private static final int BADGE_1 = AccessPoint.Speed.MODERATE;
private static final String SSID_2 = "ssid2";
private static final String BSSID_2 = "AA:AA:AA:AA:AA:AA";
@@ -102,7 +102,7 @@
new NetworkKey(new WifiKey('"' + SSID_2 + '"', BSSID_2));
private static final int RSSI_2 = -30;
private static final byte SCORE_2 = 15;
- private static final int BADGE_2 = AccessPoint.SPEED_FAST;
+ private static final int BADGE_2 = AccessPoint.Speed.FAST;
private static final int CONNECTED_NETWORK_ID = 123;
private static final int CONNECTED_RSSI = -50;
@@ -256,6 +256,7 @@
}
sendScanResultsAndProcess(tracker);
+ waitForHandlersToProcessCurrentlyEnqueuedMessages(tracker);
return tracker;
}
@@ -341,6 +342,23 @@
return createTrackerWithImmediateBroadcastsAndInjectInitialScanResults(intent);
}
+ private void waitForHandlersToProcessCurrentlyEnqueuedMessages(WifiTracker tracker)
+ throws InterruptedException {
+ CountDownLatch workerLatch = new CountDownLatch(1);
+ tracker.mWorkHandler.post(() -> {
+ workerLatch.countDown();
+ });
+ assertTrue("Latch timed out while waiting for WorkerHandler",
+ workerLatch.await(LATCH_TIMEOUT, TimeUnit.MILLISECONDS));
+
+ CountDownLatch mainLatch = new CountDownLatch(1);
+ tracker.mMainHandler.post(() -> {
+ mainLatch.countDown();
+ });
+ assertTrue("Latch timed out while waiting for MainHandler",
+ mainLatch.await(LATCH_TIMEOUT, TimeUnit.MILLISECONDS));
+ }
+
@Test
public void testAccessPointListenerSetWhenLookingUpUsingScanResults() {
ScanResult scanResult = new ScanResult();
@@ -426,7 +444,7 @@
@Test
public void startTrackingShouldSetConnectedAccessPointAsActive() throws InterruptedException {
- WifiTracker tracker = createTrackerWithScanResultsAndAccessPoint1Connected();
+ WifiTracker tracker = createTrackerWithScanResultsAndAccessPoint1Connected();
List<AccessPoint> aps = tracker.getAccessPoints();
@@ -460,16 +478,18 @@
startTracking(tracker);
sendScanResultsAndProcess(tracker);
- updateScoresAndWaitForAccessPointsChangedCallback();
+ updateScoresAndWaitForAccessPointsChangedCallback(tracker);
}
- private void updateScoresAndWaitForAccessPointsChangedCallback() throws InterruptedException {
+ private void updateScoresAndWaitForAccessPointsChangedCallback(WifiTracker tracker)
+ throws InterruptedException {
// Updating scores can happen together or one after the other, so the latch countdown is set
// to 2.
- mAccessPointsChangedLatch = new CountDownLatch(2);
+ mAccessPointsChangedLatch = new CountDownLatch(1);
updateScores();
- assertTrue("onAccessPointChanged was not called twice",
+ assertTrue("onAccessPointChanged was not called after updating scores",
mAccessPointsChangedLatch.await(LATCH_TIMEOUT, TimeUnit.MILLISECONDS));
+ waitForHandlersToProcessCurrentlyEnqueuedMessages(tracker);
}
@Test
@@ -480,7 +500,7 @@
assertEquals(aps.get(0).getSsidStr(), SSID_1);
assertEquals(aps.get(1).getSsidStr(), SSID_2);
- updateScoresAndWaitForAccessPointsChangedCallback();
+ updateScoresAndWaitForAccessPointsChangedCallback(tracker);
aps = tracker.getAccessPoints();
assertTrue(aps.size() == 2);
@@ -502,7 +522,7 @@
assertEquals(aps.get(0).getSsidStr(), SSID_1);
assertEquals(aps.get(1).getSsidStr(), SSID_2);
- updateScoresAndWaitForAccessPointsChangedCallback();
+ updateScoresAndWaitForAccessPointsChangedCallback(tracker);
aps = tracker.getAccessPoints();
assertTrue(aps.size() == 2);
@@ -514,7 +534,7 @@
public void scoreCacheUpdateScoresShouldInsertSpeedIntoAccessPoint()
throws InterruptedException {
WifiTracker tracker = createTrackerWithImmediateBroadcastsAndInjectInitialScanResults();
- updateScoresAndWaitForAccessPointsChangedCallback();
+ updateScoresAndWaitForAccessPointsChangedCallback(tracker);
List<AccessPoint> aps = tracker.getAccessPoints();
@@ -531,7 +551,7 @@
public void scoreCacheUpdateMeteredShouldUpdateAccessPointMetering()
throws InterruptedException {
WifiTracker tracker = createTrackerWithImmediateBroadcastsAndInjectInitialScanResults();
- updateScoresAndWaitForAccessPointsChangedCallback();
+ updateScoresAndWaitForAccessPointsChangedCallback(tracker);
List<AccessPoint> aps = tracker.getAccessPoints();
@@ -553,15 +573,15 @@
0 /* disabled */);
WifiTracker tracker = createTrackerWithImmediateBroadcastsAndInjectInitialScanResults();
- updateScoresAndWaitForAccessPointsChangedCallback();
+ updateScoresAndWaitForAccessPointsChangedCallback(tracker);
List<AccessPoint> aps = tracker.getAccessPoints();
for (AccessPoint ap : aps) {
if (ap.getSsidStr().equals(SSID_1)) {
- assertEquals(AccessPoint.SPEED_NONE, ap.getSpeed());
+ assertEquals(AccessPoint.Speed.NONE, ap.getSpeed());
} else if (ap.getSsidStr().equals(SSID_2)) {
- assertEquals(AccessPoint.SPEED_NONE, ap.getSpeed());
+ assertEquals(AccessPoint.Speed.NONE, ap.getSpeed());
}
}
}
@@ -754,13 +774,10 @@
throws Exception {
WifiTracker tracker = createMockedWifiTracker();
startTracking(tracker);
- tracker.stopTracking();
+ waitForHandlersToProcessCurrentlyEnqueuedMessages(tracker);
- CountDownLatch latch1 = new CountDownLatch(1);
- tracker.mMainHandler.post(() -> {
- latch1.countDown();
- });
- assertTrue("Latch 1 timed out", latch1.await(LATCH_TIMEOUT, TimeUnit.MILLISECONDS));
+ tracker.stopTracking();
+ waitForHandlersToProcessCurrentlyEnqueuedMessages(tracker);
startTracking(tracker);
@@ -770,14 +787,24 @@
tracker.mReceiver.onReceive(
mContext, new Intent(WifiManager.LINK_CONFIGURATION_CHANGED_ACTION));
- CountDownLatch latch2 = new CountDownLatch(1);
- tracker.mMainHandler.post(() -> {
- latch2.countDown();
- });
- assertTrue("Latch 2 timed out", latch2.await(LATCH_TIMEOUT, TimeUnit.MILLISECONDS));
+ waitForHandlersToProcessCurrentlyEnqueuedMessages(tracker);
verify(mockWifiListener, never()).onAccessPointsChanged();
sendScanResultsAndProcess(tracker); // verifies onAccessPointsChanged is invoked
}
+
+ @Test
+ public void disablingWifiShouldClearExistingAccessPoints() throws Exception {
+ WifiTracker tracker = createTrackerWithScanResultsAndAccessPoint1Connected();
+
+ when(mockWifiManager.isWifiEnabled()).thenReturn(false);
+ mAccessPointsChangedLatch = new CountDownLatch(1);
+ tracker.mReceiver.onReceive(mContext, new Intent(WifiManager.WIFI_STATE_CHANGED_ACTION));
+
+ mAccessPointsChangedLatch.await(LATCH_TIMEOUT, TimeUnit.MILLISECONDS);
+ waitForHandlersToProcessCurrentlyEnqueuedMessages(tracker);
+
+ assertThat(tracker.getAccessPoints()).isEmpty();
+ }
}
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/PluginActivity.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/PluginActivity.java
new file mode 100644
index 0000000..925214e
--- /dev/null
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/PluginActivity.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2017 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.systemui.plugins;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.res.Resources;
+import android.os.Bundle;
+
+import com.android.systemui.plugins.annotations.ProvidesInterface;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * A PluginActivity is an activity that replaces another full activity (e.g. RecentsActivity)
+ * at runtime within the sysui process.
+ */
+@ProvidesInterface(version = PluginActivity.VERSION)
+public abstract class PluginActivity extends Activity implements Plugin {
+
+ public static final int VERSION = 1;
+
+ public static final String ACTION_RECENTS = "com.android.systemui.action.PLUGIN_RECENTS";
+
+ private Context mSysuiContext;
+ private boolean mSettingActionBar;
+
+ @Override
+ public final void onCreate(Context sysuiContext, Context pluginContext) {
+ mSysuiContext = sysuiContext;
+ super.attachBaseContext(pluginContext);
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ Theme theme = getClass().getDeclaredAnnotation(Theme.class);
+ if (theme != null && theme.value() != 0) {
+ setTheme(theme.value());
+ }
+ mSettingActionBar = true;
+ getActionBar();
+ mSettingActionBar = false;
+ }
+
+ @Override
+ public Resources getResources() {
+ return mSettingActionBar ? mSysuiContext.getResources() : super.getResources();
+ }
+
+ @Override
+ protected void attachBaseContext(Context newBase) {
+ mSysuiContext = newBase;
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ }
+
+ public Context getSysuiContext() {
+ return mSysuiContext;
+ }
+
+ public Context getPluginContext() {
+ return getBaseContext();
+ }
+
+ /**
+ * Since PluginActivities are declared as services instead of activities (since they
+ * are plugins), they can't have a theme attached to them. Instead a PluginActivity
+ * can annotate itself with @Theme to specify the resource of the style it wants
+ * to be themed with.
+ */
+ @Retention(RetentionPolicy.RUNTIME)
+ public @interface Theme {
+ int value();
+ }
+}
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/PluginDependency.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/PluginDependency.java
index 25ce3dd..db2e376 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/PluginDependency.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/PluginDependency.java
@@ -21,6 +21,11 @@
public static final int VERSION = 1;
static DependencyProvider sProvider;
+ /**
+ * Allows a plugin to get a hold of static dependencies if they have declared dependence
+ * on their interface. For one-shot plugins this will only work during onCreate and will
+ * not work afterwards.
+ */
public static <T> T get(Plugin p, Class<T> cls) {
return sProvider.get(p, cls);
}
diff --git a/packages/SystemUI/res/drawable/recents_dismiss_dark.xml b/packages/SystemUI/res/drawable/recents_dismiss_dark.xml
index 951269b..b837ebe 100644
--- a/packages/SystemUI/res/drawable/recents_dismiss_dark.xml
+++ b/packages/SystemUI/res/drawable/recents_dismiss_dark.xml
@@ -14,11 +14,16 @@
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="48.0dp"
- android:height="48.0dp"
- android:viewportWidth="48.0"
- android:viewportHeight="48.0">
- <path
- android:fillColor="@color/recents_task_bar_dark_icon_color"
- android:pathData="M38.000000,12.800000l-2.799999,-2.800000 -11.200001,11.200001 -11.200000,-11.200001 -2.800000,2.800000 11.200001,11.200000 -11.200001,11.200001 2.800000,2.799999 11.200000,-11.200001 11.200001,11.200001 2.799999,-2.799999 -11.200001,-11.200001z"/>
+android:width="24dp"
+android:height="24dp"
+android:viewportWidth="24"
+android:viewportHeight="24">
+
+<path
+ android:fillColor="@color/recents_task_bar_dark_icon_color"
+ android:pathData="M18.3 5.71a.996 .996 0 0 0-1.41 0L12 10.59 7.11 5.7A.996 .996 0 1 0 5.7
+7.11L10.59 12 5.7 16.89a.996 .996 0 1 0 1.41 1.41L12 13.41l4.89 4.89a.996 .996 0
+1 0 1.41-1.41L13.41 12l4.89-4.89c.38-.38 .38 -1.02 0-1.4z" />
+<path
+ android:pathData="M0 0h24v24H0z" />
</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/recents_dismiss_light.xml b/packages/SystemUI/res/drawable/recents_dismiss_light.xml
index 1f44c1c..2b20814 100644
--- a/packages/SystemUI/res/drawable/recents_dismiss_light.xml
+++ b/packages/SystemUI/res/drawable/recents_dismiss_light.xml
@@ -14,11 +14,16 @@
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="48.0dp"
- android:height="48.0dp"
- android:viewportWidth="48.0"
- android:viewportHeight="48.0">
- <path
- android:fillColor="@color/recents_task_bar_light_icon_color"
- android:pathData="M38.000000,12.800000l-2.799999,-2.800000 -11.200001,11.200001 -11.200000,-11.200001 -2.800000,2.800000 11.200001,11.200000 -11.200001,11.200001 2.800000,2.799999 11.200000,-11.200001 11.200001,11.200001 2.799999,-2.799999 -11.200001,-11.200001z"/>
+android:width="24dp"
+android:height="24dp"
+android:viewportWidth="24"
+android:viewportHeight="24">
+
+<path
+ android:fillColor="@color/recents_task_bar_light_icon_color"
+ android:pathData="M18.3 5.71a.996 .996 0 0 0-1.41 0L12 10.59 7.11 5.7A.996 .996 0 1 0 5.7
+7.11L10.59 12 5.7 16.89a.996 .996 0 1 0 1.41 1.41L12 13.41l4.89 4.89a.996 .996 0
+1 0 1.41-1.41L13.41 12l4.89-4.89c.38-.38 .38 -1.02 0-1.4z" />
+<path
+ android:pathData="M0 0h24v24H0z" />
</vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/back.xml b/packages/SystemUI/res/layout/back.xml
index 4e8726b..43bec91 100644
--- a/packages/SystemUI/res/layout/back.xml
+++ b/packages/SystemUI/res/layout/back.xml
@@ -22,8 +22,10 @@
android:layout_height="match_parent"
android:layout_weight="0"
systemui:keyCode="4"
- android:scaleType="center"
+ android:scaleType="fitCenter"
android:contentDescription="@string/accessibility_back"
+ android:paddingTop="15dp"
+ android:paddingBottom="15dp"
android:paddingStart="@dimen/navigation_key_padding"
android:paddingEnd="@dimen/navigation_key_padding"
/>
diff --git a/packages/SystemUI/res/layout/home.xml b/packages/SystemUI/res/layout/home.xml
index 9586327..53ef2ab 100644
--- a/packages/SystemUI/res/layout/home.xml
+++ b/packages/SystemUI/res/layout/home.xml
@@ -21,8 +21,10 @@
android:layout_height="match_parent"
android:layout_weight="0"
systemui:keyCode="3"
- android:scaleType="center"
+ android:scaleType="fitCenter"
android:contentDescription="@string/accessibility_home"
+ android:paddingTop="13dp"
+ android:paddingBottom="13dp"
android:paddingStart="@dimen/navigation_key_padding"
android:paddingEnd="@dimen/navigation_key_padding"
/>
diff --git a/packages/SystemUI/res/layout/recent_apps.xml b/packages/SystemUI/res/layout/recent_apps.xml
index 870bcf7..c84d280 100644
--- a/packages/SystemUI/res/layout/recent_apps.xml
+++ b/packages/SystemUI/res/layout/recent_apps.xml
@@ -21,8 +21,10 @@
android:layout_width="@dimen/navigation_key_width"
android:layout_height="match_parent"
android:layout_weight="0"
- android:scaleType="center"
+ android:scaleType="fitCenter"
android:contentDescription="@string/accessibility_recent"
+ android:paddingTop="15dp"
+ android:paddingBottom="15dp"
android:paddingStart="@dimen/navigation_key_padding"
android:paddingEnd="@dimen/navigation_key_padding"
/>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 93d2072..4a1d953 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -827,7 +827,4 @@
<!-- How far to inset the rounded edges -->
<dimen name="stat_sys_mobile_signal_circle_inset">0.9dp</dimen>
- <!-- Width of the hollow triangle for empty signal state -->
- <dimen name="mobile_signal_empty_strokewidth">2dp</dimen>
-
</resources>
diff --git a/packages/SystemUI/src/com/android/systemui/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index 1b694b3..bb44123 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -31,6 +31,7 @@
import com.android.systemui.colorextraction.SysuiColorExtractor;
import com.android.systemui.fragments.FragmentService;
import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.PluginActivityManager;
import com.android.systemui.plugins.PluginDependencyProvider;
import com.android.systemui.plugins.PluginManager;
import com.android.systemui.plugins.PluginManagerImpl;
@@ -276,6 +277,9 @@
mProviders.put(UiOffloadThread.class, UiOffloadThread::new);
+ mProviders.put(PluginActivityManager.class,
+ () -> new PluginActivityManager(mContext, getDependency(PluginManager.class)));
+
// Put all dependencies above here so the factory can override them if it wants.
SystemUIFactory.getInstance().injectDependencies(mProviders, mContext);
}
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
index 4a45997..fe6eb4b 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
@@ -16,6 +16,7 @@
package com.android.systemui;
+import android.app.Activity;
import android.app.ActivityThread;
import android.app.Application;
import android.content.BroadcastReceiver;
@@ -39,6 +40,7 @@
import com.android.systemui.plugins.GlobalActions;
import com.android.systemui.plugins.OverlayPlugin;
import com.android.systemui.plugins.Plugin;
+import com.android.systemui.plugins.PluginActivityManager;
import com.android.systemui.plugins.PluginListener;
import com.android.systemui.plugins.PluginManager;
import com.android.systemui.power.PowerUI;
@@ -266,4 +268,10 @@
public SystemUI[] getServices() {
return mServices;
}
+
+ @Override
+ public Activity instantiateActivity(ClassLoader cl, String className, Intent intent) {
+ if (!mServicesStarted) return null;
+ return Dependency.get(PluginActivityManager.class).instantiate(cl, className, intent);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index ae936db..8d1d6e0 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -39,6 +39,7 @@
import com.android.systemui.util.wakelock.WakeLock;
import java.io.PrintWriter;
+import java.util.function.IntConsumer;
/**
* Handles triggers for ambient state changes.
@@ -98,18 +99,44 @@
requestPulse(DozeLog.PULSE_REASON_NOTIFICATION, false /* performedProxCheck */);
}
+ private void proximityCheckThenCall(IntConsumer callback,
+ boolean alreadyPerformedProxCheck,
+ int pulseReason) {
+ if (alreadyPerformedProxCheck) {
+ callback.accept(ProximityCheck.RESULT_NOT_CHECKED);
+ } else {
+ final long start = SystemClock.uptimeMillis();
+ new ProximityCheck() {
+ @Override
+ public void onProximityResult(int result) {
+ final long end = SystemClock.uptimeMillis();
+ DozeLog.traceProximityResult(mContext, result == RESULT_NEAR,
+ end - start, pulseReason);
+ callback.accept(result);
+ }
+ }.check();
+ }
+ }
+
private void onSensor(int pulseReason, boolean sensorPerformedProxCheck,
float screenX, float screenY) {
boolean isDoubleTap = pulseReason == DozeLog.PULSE_REASON_SENSOR_DOUBLE_TAP;
boolean isPickup = pulseReason == DozeLog.PULSE_REASON_SENSOR_PICKUP;
if (mConfig.alwaysOnEnabled(UserHandle.USER_CURRENT)) {
- if (isDoubleTap) {
- mDozeHost.onDoubleTap(screenX, screenY);
- mMachine.wakeUp();
- } else {
- mDozeHost.extendPulse();
- }
+ proximityCheckThenCall((result) -> {
+ if (result == ProximityCheck.RESULT_NEAR) {
+ // In pocket, drop event.
+ return;
+ }
+ if (isDoubleTap) {
+ mDozeHost.onDoubleTap(screenX, screenY);
+ mMachine.wakeUp();
+ } else {
+ mDozeHost.extendPulse();
+ }
+ }, sensorPerformedProxCheck, pulseReason);
+ return;
} else {
requestPulse(pulseReason, sensorPerformedProxCheck);
}
@@ -158,13 +185,16 @@
break;
case DOZE:
case DOZE_AOD:
- case DOZE_AOD_PAUSED:
mDozeSensors.setProxListening(newState != DozeMachine.State.DOZE);
mDozeSensors.setListening(true);
if (oldState != DozeMachine.State.INITIALIZED) {
mDozeSensors.reregisterAllSensors();
}
break;
+ case DOZE_AOD_PAUSED:
+ mDozeSensors.setProxListening(true);
+ mDozeSensors.setListening(false);
+ break;
case DOZE_PULSING:
mDozeSensors.setProxListening(true);
break;
@@ -199,33 +229,15 @@
}
mPulsePending = true;
- if (!mDozeParameters.getProxCheckBeforePulse() || performedProxCheck) {
- // skip proximity check
- continuePulseRequest(reason);
- return;
- }
-
- final long start = SystemClock.uptimeMillis();
- new ProximityCheck() {
- @Override
- public void onProximityResult(int result) {
- final long end = SystemClock.uptimeMillis();
- DozeLog.traceProximityResult(mContext, result == RESULT_NEAR,
- end - start, reason);
- if (performedProxCheck) {
- // we already continued
- return;
- }
- // avoid pulsing in pockets
- if (result == RESULT_NEAR) {
- mPulsePending = false;
- return;
- }
-
- // not in-pocket, continue pulsing
+ proximityCheckThenCall((result) -> {
+ if (result == ProximityCheck.RESULT_NEAR) {
+ // in pocket, abort pulse
+ mPulsePending = false;
+ } else {
+ // not in pocket, continue pulsing
continuePulseRequest(reason);
}
- }.check();
+ }, !mDozeParameters.getProxCheckBeforePulse() || performedProxCheck, reason);
}
private boolean canPulse() {
@@ -259,6 +271,7 @@
protected static final int RESULT_UNKNOWN = 0;
protected static final int RESULT_NEAR = 1;
protected static final int RESULT_FAR = 2;
+ protected static final int RESULT_NOT_CHECKED = 3;
private boolean mRegistered;
private boolean mFinished;
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index 80a6418..1a8a474 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -339,6 +339,7 @@
long id) {
final Action action = mAdapter.getItem(position);
if (action instanceof LongPressAction) {
+ mDialog.dismiss();
return ((LongPressAction) action).onLongPress();
}
return false;
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
index b8771d7..cebb22f 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipMotionHelper.java
@@ -241,14 +241,14 @@
/**
* Flings the minimized PiP to the closest minimized snap target.
*/
- Rect flingToMinimizedState(float velocityY, Rect movementBounds) {
+ Rect flingToMinimizedState(float velocityY, Rect movementBounds, Point dragStartPosition) {
cancelAnimations();
// We currently only allow flinging the minimized stack up and down, so just lock the
// movement bounds to the current stack bounds horizontally
movementBounds = new Rect(mBounds.left, movementBounds.top, mBounds.left,
movementBounds.bottom);
Rect toBounds = mSnapAlgorithm.findClosestSnapBounds(movementBounds, mBounds,
- 0 /* velocityX */, velocityY);
+ 0 /* velocityX */, velocityY, dragStartPosition);
if (!mBounds.equals(toBounds)) {
mBoundsAnimator = createAnimationToBounds(mBounds, toBounds, 0, FAST_OUT_SLOW_IN);
mFlingAnimationUtils.apply(mBoundsAnimator, 0,
@@ -281,10 +281,11 @@
* Flings the PiP to the closest snap target.
*/
Rect flingToSnapTarget(float velocity, float velocityX, float velocityY, Rect movementBounds,
- AnimatorUpdateListener updateListener, AnimatorListener listener) {
+ AnimatorUpdateListener updateListener, AnimatorListener listener,
+ Point startPosition) {
cancelAnimations();
Rect toBounds = mSnapAlgorithm.findClosestSnapBounds(movementBounds, mBounds,
- velocityX, velocityY);
+ velocityX, velocityY, startPosition);
if (!mBounds.equals(toBounds)) {
mBoundsAnimator = createAnimationToBounds(mBounds, toBounds, 0, FAST_OUT_SLOW_IN);
mFlingAnimationUtils.apply(mBoundsAnimator, 0,
diff --git a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
index 3682ae6..9588b03 100644
--- a/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/pip/phone/PipTouchHandler.java
@@ -185,7 +185,7 @@
mDismissViewController = new PipDismissViewController(context);
mSnapAlgorithm = new PipSnapAlgorithm(mContext);
mTouchState = new PipTouchState(mViewConfig);
- mFlingAnimationUtils = new FlingAnimationUtils(context, 2f);
+ mFlingAnimationUtils = new FlingAnimationUtils(context, 2.5f);
mGestures = new PipTouchGesture[] {
mDefaultMovementGesture
};
@@ -534,6 +534,7 @@
private PipTouchGesture mDefaultMovementGesture = new PipTouchGesture() {
// Whether the PiP was on the left side of the screen at the start of the gesture
private boolean mStartedOnLeft;
+ private Point mStartPosition;
@Override
public void onDown(PipTouchState touchState) {
@@ -541,7 +542,9 @@
return;
}
- mStartedOnLeft = mMotionHelper.getBounds().left < mMovementBounds.centerX();
+ Rect bounds = mMotionHelper.getBounds();
+ mStartPosition = new Point(bounds.left, bounds.top);
+ mStartedOnLeft = bounds.left < mMovementBounds.centerX();
mMovementWithinMinimize = true;
mMovementWithinDismiss = touchState.getDownTouchPosition().y >= mMovementBounds.bottom;
@@ -687,7 +690,8 @@
if (isFling) {
mMotionHelper.flingToSnapTarget(velocity, vel.x, vel.y, mMovementBounds,
- mUpdateScrimListener, postAnimationCallback);
+ mUpdateScrimListener, postAnimationCallback,
+ mStartPosition);
} else {
mMotionHelper.animateToClosestSnapTarget(mMovementBounds, mUpdateScrimListener,
postAnimationCallback);
diff --git a/packages/SystemUI/src/com/android/systemui/plugins/PluginActivityManager.java b/packages/SystemUI/src/com/android/systemui/plugins/PluginActivityManager.java
new file mode 100644
index 0000000..9becc38
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/plugins/PluginActivityManager.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2017 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.systemui.plugins;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.text.TextUtils;
+import android.util.ArrayMap;
+
+public class PluginActivityManager {
+
+ private final Context mContext;
+ private final PluginManager mPluginManager;
+ private final ArrayMap<String, String> mActionLookup = new ArrayMap<>();
+
+ public PluginActivityManager(Context context, PluginManager pluginManager) {
+ mContext = context;
+ mPluginManager = pluginManager;
+ }
+
+ public void addActivityPlugin(String className, String action) {
+ mActionLookup.put(className, action);
+ }
+
+ public Activity instantiate(ClassLoader cl, String className, Intent intent) {
+ String action = mActionLookup.get(className);
+ if (TextUtils.isEmpty(action)) return null;
+ return mPluginManager.getOneShotPlugin(action, PluginActivity.class);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/plugins/PluginManagerImpl.java b/packages/SystemUI/src/com/android/systemui/plugins/PluginManagerImpl.java
index 493d244..a968399 100644
--- a/packages/SystemUI/src/com/android/systemui/plugins/PluginManagerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/plugins/PluginManagerImpl.java
@@ -42,7 +42,6 @@
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
import com.android.systemui.Dependency;
import com.android.systemui.plugins.PluginInstanceManager.PluginContextWrapper;
-import com.android.systemui.plugins.PluginInstanceManager.PluginInfo;
import com.android.systemui.plugins.annotations.ProvidesInterface;
import dalvik.system.PathClassLoader;
@@ -120,14 +119,21 @@
}
PluginInstanceManager<T> p = mFactory.createPluginInstanceManager(mContext, action, null,
false, mLooper, cls, this);
+ PluginListener<Plugin> listener = new PluginListener<Plugin>() {
+ @Override
+ public void onPluginConnected(Plugin plugin, Context pluginContext) { }
+ };
+ mPluginMap.put(listener, p);
mPluginPrefs.addAction(action);
- PluginInfo<T> info = p.getPlugin();
+ PluginInstanceManager.PluginInfo<T> info = p.getPlugin();
if (info != null) {
mOneShotPackages.add(info.mPackage);
mHasOneShot = true;
startListening();
+ mPluginMap.remove(listener);
return info.mPlugin;
}
+ mPluginMap.remove(listener);
return null;
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
index 9ba32b3..de2ace4 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Recents.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
@@ -43,11 +43,14 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.systemui.Dependency;
import com.android.systemui.EventLogConstants;
import com.android.systemui.EventLogTags;
import com.android.systemui.R;
import com.android.systemui.RecentsComponent;
import com.android.systemui.SystemUI;
+import com.android.systemui.plugins.PluginActivity;
+import com.android.systemui.plugins.PluginActivityManager;
import com.android.systemui.recents.events.EventBus;
import com.android.systemui.recents.events.activity.ConfigurationChangedEvent;
import com.android.systemui.recents.events.activity.DockedTopTaskEvent;
@@ -234,6 +237,8 @@
registerWithSystemUser();
}
putComponent(Recents.class, this);
+ Dependency.get(PluginActivityManager.class).addActivityPlugin(RecentsImpl.RECENTS_ACTIVITY,
+ PluginActivity.ACTION_RECENTS);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
index eec818b..29b720c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ActivatableNotificationView.java
@@ -943,6 +943,10 @@
}
}
+ public boolean isDrawingAppearAnimation() {
+ return mDrawingAppearAnimation;
+ }
+
@Override
protected void dispatchDraw(Canvas canvas) {
if (mDrawingAppearAnimation) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
index 6abf35f..ed4f685 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ExpandableNotificationRow.java
@@ -756,6 +756,19 @@
return getShowingLayout().getVisibleNotificationHeader();
}
+
+ /**
+ * @return the contracted notification header. This can be different from
+ * {@link #getNotificationHeader()} and also {@link #getVisibleNotificationHeader()} and only
+ * returns the contracted version.
+ */
+ public NotificationHeaderView getContractedNotificationHeader() {
+ if (mIsSummaryWithChildren) {
+ return mChildrenContainer.getHeaderView();
+ }
+ return mPrivateLayout.getContractedNotificationHeader();
+ }
+
public void setOnExpandClickListener(OnExpandClickListener onExpandClickListener) {
mOnExpandClickListener = onExpandClickListener;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
index 75704b16..9e059c89 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationContentView.java
@@ -1308,6 +1308,14 @@
return header;
}
+
+ public NotificationHeaderView getContractedNotificationHeader() {
+ if (mContractedChild != null) {
+ return mContractedWrapper.getNotificationHeader();
+ }
+ return null;
+ }
+
public NotificationHeaderView getVisibleNotificationHeader() {
NotificationViewWrapper wrapper = getVisibleWrapper(mVisibleType);
return wrapper == null ? null : wrapper.getNotificationHeader();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderUtil.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderUtil.java
index 7f95d48..4301817 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderUtil.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationHeaderUtil.java
@@ -267,9 +267,10 @@
if (!mApply) {
return;
}
- NotificationHeaderView header = row.getNotificationHeader();
+ NotificationHeaderView header = row.getContractedNotificationHeader();
if (header == null) {
- mApply = false;
+ // No header found. We still consider this to be the same to avoid weird flickering
+ // when for example showing an undo notification
return;
}
Object childData = mExtractor == null ? null : mExtractor.extractData(row);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index 97e2f6d..a601028 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -16,15 +16,18 @@
package com.android.systemui.statusbar;
+import static com.android.systemui.statusbar.phone.NotificationIconContainer.IconState.NO_VALUE;
+import static com.android.systemui.statusbar.phone.NotificationIconContainer.OVERFLOW_EARLY_AMOUNT;
+
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.os.SystemProperties;
import android.util.AttributeSet;
-import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityNodeInfo;
+
import com.android.systemui.Interpolators;
import com.android.systemui.R;
import com.android.systemui.ViewInvertHelper;
@@ -315,26 +318,65 @@
private float updateIconAppearance(ExpandableNotificationRow row, float expandAmount,
boolean scrolling, boolean scrollingFast, boolean expandingAnimated,
boolean isLastChild) {
+ StatusBarIconView icon = row.getEntry().expandedIcon;
+ NotificationIconContainer.IconState iconState = getIconState(icon);
+ if (iconState == null) {
+ return 0.0f;
+ }
+
// Let calculate how much the view is in the shelf
float viewStart = row.getTranslationY();
int fullHeight = row.getActualHeight() + mPaddingBetweenElements;
float iconTransformDistance = getIntrinsicHeight() * 1.5f;
iconTransformDistance *= NotificationUtils.interpolate(1.f, 1.5f, expandAmount);
+ iconTransformDistance = Math.min(iconTransformDistance, fullHeight);
if (isLastChild) {
fullHeight = Math.min(fullHeight, row.getMinHeight() - getIntrinsicHeight());
iconTransformDistance = Math.min(iconTransformDistance, row.getMinHeight()
- getIntrinsicHeight());
}
float viewEnd = viewStart + fullHeight;
+ if (expandingAnimated && mAmbientState.getScrollY() == 0
+ && !mAmbientState.isOnKeyguard() && !iconState.isLastExpandIcon) {
+ // We are expanding animated. Because we switch to a linear interpolation in this case,
+ // the last icon may be stuck in between the shelf position and the notification
+ // position, which looks pretty bad. We therefore optimize this case by applying a
+ // shorter transition such that the icon is either fully in the notification or we clamp
+ // it into the shelf if it's close enough.
+ // We need to persist this, since after the expansion, the behavior should still be the
+ // same.
+ float position = mAmbientState.getIntrinsicPadding()
+ + mHostLayout.getPositionInLinearLayout(row);
+ int maxShelfStart = mMaxLayoutHeight - getIntrinsicHeight();
+ if (position < maxShelfStart && position + row.getIntrinsicHeight() >= maxShelfStart
+ && row.getTranslationY() < position) {
+ iconState.isLastExpandIcon = true;
+ iconState.customTransformHeight = NO_VALUE;
+ // Let's check if we're close enough to snap into the shelf
+ boolean forceInShelf = mMaxLayoutHeight - getIntrinsicHeight() - position
+ < getIntrinsicHeight();
+ if (!forceInShelf) {
+ // We are overlapping the shelf but not enough, so the icon needs to be
+ // repositioned
+ iconState.customTransformHeight = (int) (mMaxLayoutHeight
+ - getIntrinsicHeight() - position);
+ }
+ }
+ }
float fullTransitionAmount;
float iconTransitionAmount;
float shelfStart = getTranslationY();
+ if (iconState.hasCustomTransformHeight()) {
+ fullHeight = iconState.customTransformHeight;
+ iconTransformDistance = iconState.customTransformHeight;
+ }
+ boolean fullyInOrOut = true;
if (viewEnd >= shelfStart && (!mAmbientState.isUnlockHintRunning() || row.isInShelf())
&& (mAmbientState.isShadeExpanded()
|| (!row.isPinned() && !row.isHeadsUpAnimatingAway()))) {
if (viewStart < shelfStart) {
-
float fullAmount = (shelfStart - viewStart) / fullHeight;
+ fullAmount = Math.min(1.0f, fullAmount);
float interpolatedAmount = Interpolators.ACCELERATE_DECELERATE.getInterpolation(
fullAmount);
interpolatedAmount = NotificationUtils.interpolate(
@@ -344,7 +386,7 @@
iconTransitionAmount = (shelfStart - viewStart) / iconTransformDistance;
iconTransitionAmount = Math.min(1.0f, iconTransitionAmount);
iconTransitionAmount = 1.0f - iconTransitionAmount;
-
+ fullyInOrOut = false;
} else {
fullTransitionAmount = 1.0f;
iconTransitionAmount = 1.0f;
@@ -353,6 +395,10 @@
fullTransitionAmount = 0.0f;
iconTransitionAmount = 0.0f;
}
+ if (fullyInOrOut && !expandingAnimated && iconState.isLastExpandIcon) {
+ iconState.isLastExpandIcon = false;
+ iconState.customTransformHeight = NO_VALUE;
+ }
updateIconPositioning(row, iconTransitionAmount, fullTransitionAmount,
iconTransformDistance, scrolling, scrollingFast, expandingAnimated, isLastChild);
return fullTransitionAmount;
@@ -366,9 +412,10 @@
if (iconState == null) {
return;
}
+ boolean forceInShelf = iconState.isLastExpandIcon && !iconState.hasCustomTransformHeight();
float clampedAmount = iconTransitionAmount > 0.5f ? 1.0f : 0.0f;
if (clampedAmount == fullTransitionAmount) {
- iconState.noAnimations = scrollingFast || expandingAnimated;
+ iconState.noAnimations = (scrollingFast || expandingAnimated) && !forceInShelf;
iconState.useFullTransitionAmount = iconState.noAnimations
|| (!ICON_ANMATIONS_WHILE_SCROLLING && fullTransitionAmount == 0.0f && scrolling);
iconState.useLinearTransitionAmount = !ICON_ANMATIONS_WHILE_SCROLLING
@@ -376,12 +423,18 @@
iconState.translateContent = mMaxLayoutHeight - getTranslationY()
- getIntrinsicHeight() > 0;
}
- if (scrollingFast || (expandingAnimated && iconState.useFullTransitionAmount
- && !ViewState.isAnimatingY(icon))) {
+ if (!forceInShelf && (scrollingFast || (expandingAnimated
+ && iconState.useFullTransitionAmount && !ViewState.isAnimatingY(icon)))) {
iconState.cancelAnimations(icon);
iconState.useFullTransitionAmount = true;
iconState.noAnimations = true;
}
+ if (iconState.hasCustomTransformHeight()) {
+ iconState.useFullTransitionAmount = true;
+ }
+ if (iconState.isLastExpandIcon) {
+ iconState.translateContent = false;
+ }
float transitionAmount;
if (isLastChild || !USE_ANIMATIONS_WHEN_OPENING || iconState.useFullTransitionAmount
|| iconState.useLinearTransitionAmount) {
@@ -452,6 +505,11 @@
iconState.scaleX = newSize / icon.getHeight() / icon.getIconScale();
iconState.scaleY = iconState.scaleX;
iconState.hidden = transitionAmount == 0.0f && !iconState.isAnimating(icon);
+ boolean isAppearing = row.isDrawingAppearAnimation() && !row.isInShelf();
+ if (isAppearing) {
+ iconState.hidden = true;
+ iconState.iconAppearAmount = 0.0f;
+ }
iconState.alpha = alpha;
iconState.yTranslation = iconYTranslation;
if (stayingInShelf) {
@@ -548,8 +606,7 @@
if (!hasOverflow) {
// we have to ensure that adding the low priority notification won't lead to an
// overflow
- collapsedPadding -= (1.0f + NotificationIconContainer.OVERFLOW_EARLY_AMOUNT)
- * mCollapsedIcons.getIconSize();
+ collapsedPadding -= (1.0f + OVERFLOW_EARLY_AMOUNT) * mCollapsedIcons.getIconSize();
}
float padding = NotificationUtils.interpolate(collapsedPadding,
mShelfIcons.getPaddingEnd(),
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java
index a14d1bc..09ae521 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarInflaterView.java
@@ -37,6 +37,7 @@
import com.android.systemui.plugins.PluginListener;
import com.android.systemui.plugins.PluginManager;
import com.android.systemui.plugins.statusbar.phone.NavBarButtonProvider;
+import com.android.systemui.statusbar.phone.ReverseLinearLayout.ReverseFrameLayout;
import com.android.systemui.statusbar.policy.KeyButtonView;
import com.android.systemui.tuner.TunerService;
import com.android.systemui.tuner.TunerService.Tunable;
@@ -285,7 +286,7 @@
if (sizeStr.contains(WEIGHT_SUFFIX)) {
float weight = Float.parseFloat(sizeStr.substring(0, sizeStr.indexOf(WEIGHT_SUFFIX)));
- FrameLayout frame = new FrameLayout(mContext);
+ FrameLayout frame = new ReverseFrameLayout(mContext);
LayoutParams childParams = new LayoutParams(v.getLayoutParams());
if (sizeStr.endsWith(WEIGHT_CENTERED_SUFFIX)) {
childParams.gravity = Gravity.CENTER;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
index a6cd472..4264441 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NavigationBarView.java
@@ -575,15 +575,17 @@
mRotatedViews[Surface.ROTATION_270] =
mRotatedViews[Surface.ROTATION_90] = findViewById(R.id.rot90);
- updateCurrentView();
+ mCurrentRotation = -1;
+ reorient();
}
public boolean needsReorient(int rotation) {
return mCurrentRotation != rotation;
}
- private void updateCurrentView() {
+ private boolean updateCurrentView() {
final int rot = mDisplay.getRotation();
+ if (rot == mCurrentRotation) return false;
for (int i=0; i<4; i++) {
mRotatedViews[i].setVisibility(View.GONE);
}
@@ -595,6 +597,7 @@
}
updateLayoutTransitionsEnabled();
mCurrentRotation = rot;
+ return true;
}
private void updateRecentsIcon() {
@@ -607,10 +610,14 @@
}
public void reorient() {
- updateCurrentView();
+ if (!updateCurrentView()) {
+ return;
+ }
mDeadZone = (DeadZone) mCurrentView.findViewById(R.id.deadzone);
- ((NavigationBarFrame) getRootView()).setDeadZone(mDeadZone);
+ if (getRootView() instanceof NavigationBarFrame) {
+ ((NavigationBarFrame) getRootView()).setDeadZone(mDeadZone);
+ }
mDeadZone.setDisplayRotation(mCurrentRotation);
// force the low profile & disabled states into compliance
@@ -644,6 +651,7 @@
mVertical = newVertical;
//Log.v(TAG, String.format("onSizeChanged: h=%d, w=%d, vert=%s", h, w, mVertical?"y":"n"));
reorient();
+ getHomeButton().setVertical(mVertical);
notifyVerticalChangedListener(newVertical);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
index 021e11a..6f5ad96 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
@@ -526,6 +526,7 @@
}
public class IconState extends ViewState {
+ public static final int NO_VALUE = NotificationIconContainer.NO_VALUE;
public float iconAppearAmount = 1.0f;
public float clampedAppearAmount = 1.0f;
public int visibleState;
@@ -538,6 +539,8 @@
public boolean justUndarkened;
public int iconColor = StatusBarIconView.NO_COLOR;
public boolean noAnimations;
+ public boolean isLastExpandIcon;
+ public int customTransformHeight = NO_VALUE;
@Override
public void applyToView(View view) {
@@ -615,6 +618,10 @@
justUndarkened = false;
}
+ public boolean hasCustomTransformHeight() {
+ return isLastExpandIcon && customTransformHeight != NO_VALUE;
+ }
+
@Override
public void initFrom(View view) {
super.initFrom(view);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ReverseLinearLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ReverseLinearLayout.java
index f45967a..bcbc345 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ReverseLinearLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ReverseLinearLayout.java
@@ -16,10 +16,10 @@
import android.annotation.Nullable;
import android.content.Context;
-import android.content.res.Configuration;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
+import android.widget.FrameLayout;
import android.widget.LinearLayout;
import java.util.ArrayList;
@@ -48,7 +48,7 @@
@Override
public void addView(View child) {
- reversParams(child.getLayoutParams());
+ reverseParams(child.getLayoutParams(), child);
if (mIsLayoutReverse) {
super.addView(child, 0);
} else {
@@ -58,7 +58,7 @@
@Override
public void addView(View child, ViewGroup.LayoutParams params) {
- reversParams(params);
+ reverseParams(params, child);
if (mIsLayoutReverse) {
super.addView(child, 0, params);
} else {
@@ -100,7 +100,15 @@
}
}
- private void reversParams(ViewGroup.LayoutParams params) {
+ private static void reverseParams(ViewGroup.LayoutParams params, View child) {
+ if (child instanceof Reversable) {
+ ((Reversable) child).reverse();
+ }
+ if (child.getPaddingLeft() == child.getPaddingRight()
+ && child.getPaddingTop() == child.getPaddingBottom()) {
+ child.setPadding(child.getPaddingTop(), child.getPaddingLeft(),
+ child.getPaddingTop(), child.getPaddingLeft());
+ }
if (params == null) {
return;
}
@@ -109,4 +117,23 @@
params.height = width;
}
+ public interface Reversable {
+ void reverse();
+ }
+
+ public static class ReverseFrameLayout extends FrameLayout implements Reversable {
+
+ public ReverseFrameLayout(Context context) {
+ super(context);
+ }
+
+ @Override
+ public void reverse() {
+ for (int i = 0; i < getChildCount(); i++) {
+ View child = getChildAt(i);
+ reverseParams(child.getLayoutParams(), child);
+ }
+ }
+ }
+
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SignalDrawable.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SignalDrawable.java
index deea521..7a7efbd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SignalDrawable.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SignalDrawable.java
@@ -100,12 +100,8 @@
// How far the circle defining the corners is inset from the edges
private final float mAppliedCornerInset;
- // The easiest way to understand this is as if we set Style.STROKE and draw the triangle,
- // but that is only theoretically right. Instead, draw the triangle and clip out a smaller
- // one inset by this amount.
- private final float mEmptyStrokeWidth;
private static final float INV_TAN = 1f / (float) Math.tan(Math.PI / 8f);
- private final float mEmptyDiagInset; // == mEmptyStrokeWidth * INV_TAN
+ private static final float CUT_WIDTH_DP = 1f / 12f;
// Where the top and left points of the triangle would be if not for rounding
private final PointF mVirtualTop = new PointF();
@@ -145,11 +141,6 @@
Utils.getDefaultColor(context, R.color.light_mode_icon_color_dual_tone_fill);
mIntrinsicSize = context.getResources().getDimensionPixelSize(R.dimen.signal_icon_size);
- // mCutPath parameters
- mEmptyStrokeWidth = context.getResources()
- .getDimensionPixelSize(R.dimen.mobile_signal_empty_strokewidth);
- mEmptyDiagInset = mEmptyStrokeWidth * INV_TAN;
-
mHandler = new Handler();
setDarkIntensity(0);
@@ -326,22 +317,20 @@
(padding + cornerRadius + mAppliedCornerInset) - (INV_TAN * cornerRadius),
height - padding);
+ final float cutWidth = CUT_WIDTH_DP * height;
+ final float cutDiagInset = cutWidth * INV_TAN;
+
// Cut out a smaller triangle from the center of mFullPath
mCutPath.reset();
mCutPath.setFillType(FillType.WINDING);
- mCutPath.moveTo(width - padding - mEmptyStrokeWidth,
- height - padding - mEmptyStrokeWidth);
- mCutPath.lineTo(width - padding - mEmptyStrokeWidth,
- mVirtualTop.y + mEmptyDiagInset);
- mCutPath.lineTo(mVirtualLeft.x + mEmptyDiagInset,
- height - padding - mEmptyStrokeWidth);
- mCutPath.lineTo(width - padding - mEmptyStrokeWidth,
- height - padding - mEmptyStrokeWidth);
+ mCutPath.moveTo(width - padding - cutWidth, height - padding - cutWidth);
+ mCutPath.lineTo(width - padding - cutWidth, mVirtualTop.y + cutDiagInset);
+ mCutPath.lineTo(mVirtualLeft.x + cutDiagInset, height - padding - cutWidth);
+ mCutPath.lineTo(width - padding - cutWidth, height - padding - cutWidth);
- // In empty state, draw the full path as the foreground paint
- mForegroundPath.set(mFullPath);
- mFullPath.reset();
- mForegroundPath.op(mCutPath, Path.Op.DIFFERENCE);
+ // Draw empty state as only background
+ mForegroundPath.reset();
+ mFullPath.op(mCutPath, Path.Op.DIFFERENCE);
} else if (mState == STATE_AIRPLANE) {
// Airplane mode is slashed, full-signal
mForegroundPath.set(mFullPath);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java
index b7b991e..ba1e7c2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/AmbientState.java
@@ -62,6 +62,7 @@
private boolean mHasPulsingNotifications;
private boolean mUnlockHintRunning;
private boolean mQsCustomizerShowing;
+ private int mIntrinsicPadding;
public AmbientState(Context context) {
reload(context);
@@ -323,4 +324,12 @@
public void setQsCustomizerShowing(boolean qsCustomizerShowing) {
mQsCustomizerShowing = qsCustomizerShowing;
}
+
+ public void setIntrinsicPadding(int intrinsicPadding) {
+ mIntrinsicPadding = intrinsicPadding;
+ }
+
+ public int getIntrinsicPadding() {
+ return mIntrinsicPadding;
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
index cbd315b..41cde9c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -2735,7 +2735,7 @@
return view.getHeight();
}
- private int getPositionInLinearLayout(View requestedView) {
+ public int getPositionInLinearLayout(View requestedView) {
ExpandableNotificationRow childInGroup = null;
ExpandableNotificationRow requestedRow = null;
if (isChildInGroup(requestedView)) {
@@ -3650,6 +3650,7 @@
public void setIntrinsicPadding(int intrinsicPadding) {
mIntrinsicPadding = intrinsicPadding;
+ mAmbientState.setIntrinsicPadding(intrinsicPadding);
}
public int getIntrinsicPadding() {
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
index 5d51a33..eaad2f9 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogControllerImpl.java
@@ -43,6 +43,7 @@
import android.service.notification.Condition;
import android.util.ArrayMap;
import android.util.Log;
+import android.view.accessibility.AccessibilityManager;
import com.android.internal.annotations.GuardedBy;
import com.android.systemui.Dumpable;
@@ -122,6 +123,12 @@
mReceiver.init();
mVibrator = (Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE);
mHasVibrator = mVibrator != null && mVibrator.hasVibrator();
+
+ boolean accessibilityVolumeStreamActive = context.getSystemService(
+ AccessibilityManager.class).isAccessibilityVolumeStreamActive();
+ mVolumeController.setA11yMode(accessibilityVolumeStreamActive ?
+ VolumePolicy.A11Y_MODE_INDEPENDENT_A11Y_VOLUME :
+ VolumePolicy.A11Y_MODE_MEDIA_A11Y_VOLUME);
}
public AudioManager getAudioManager() {
@@ -210,6 +217,7 @@
public void addCallback(Callbacks callback, Handler handler) {
mCallbacks.add(callback, handler);
+ callback.onAccessibilityModeChanged(mShowA11yStream);
}
public void setUserActivityListener(UserActivityListener listener) {
diff --git a/packages/SystemUI/tests/AndroidManifest.xml b/packages/SystemUI/tests/AndroidManifest.xml
index b12fd1c..9bb2180 100644
--- a/packages/SystemUI/tests/AndroidManifest.xml
+++ b/packages/SystemUI/tests/AndroidManifest.xml
@@ -48,7 +48,7 @@
android:process=":killable" />
</application>
- <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+ <instrumentation android:name="android.testing.TestableInstrumentation"
android:targetPackage="com.android.systemui.tests"
android:label="Tests for SystemUI">
</instrumentation>
diff --git a/packages/SystemUI/tests/src/com/android/systemui/RoundedCornersTest.java b/packages/SystemUI/tests/src/com/android/systemui/RoundedCornersTest.java
index 945af34..13ed2a2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/RoundedCornersTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/RoundedCornersTest.java
@@ -21,7 +21,6 @@
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
@@ -30,7 +29,6 @@
import static org.mockito.Mockito.when;
import android.app.Fragment;
-import android.support.test.filters.FlakyTest;
import android.support.test.filters.SmallTest;
import android.testing.AndroidTestingRunner;
import android.view.Display;
@@ -53,7 +51,6 @@
import org.junit.runner.RunWith;
@RunWith(AndroidTestingRunner.class)
-@FlakyTest
@SmallTest
public class RoundedCornersTest extends SysuiTestCase {
@@ -72,6 +69,7 @@
mWindowManager = mock(WindowManager.class);
mView = spy(new StatusBarWindowView(mContext, null));
when(mStatusBar.getStatusBarWindow()).thenReturn(mView);
+ when(mStatusBar.getNavigationBarWindow()).thenReturn(mView);
mContext.putComponent(StatusBar.class, mStatusBar);
Display display = mContext.getSystemService(WindowManager.class).getDefaultDisplay();
@@ -94,6 +92,8 @@
@Test
public void testNoRounding() {
mContext.getOrCreateTestableResources().addOverride(dimen.rounded_corner_radius, 0);
+ mContext.getOrCreateTestableResources()
+ .addOverride(dimen.rounded_corner_content_padding, 0);
mRoundedCorners.start();
// No views added.
@@ -105,8 +105,11 @@
}
@Test
+ @Ignore
public void testRounding() {
mContext.getOrCreateTestableResources().addOverride(dimen.rounded_corner_radius, 20);
+ mContext.getOrCreateTestableResources()
+ .addOverride(dimen.rounded_corner_content_padding, 20);
mRoundedCorners.start();
// Add 2 windows for rounded corners (top and bottom).
diff --git a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
index 361a20f..66d00dd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/SysuiTestCase.java
@@ -31,6 +31,8 @@
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
@@ -56,10 +58,14 @@
mRealInstrumentation = InstrumentationRegistry.getInstrumentation();
Instrumentation inst = spy(mRealInstrumentation);
- when(inst.getContext()).thenThrow(new RuntimeException(
- "SysUI Tests should use SysuiTestCase#getContext or SysuiTestCase#mContext"));
- when(inst.getTargetContext()).thenThrow(new RuntimeException(
- "SysUI Tests should use SysuiTestCase#getContext or SysuiTestCase#mContext"));
+ when(inst.getContext()).thenAnswer(invocation -> {
+ throw new RuntimeException(
+ "SysUI Tests should use SysuiTestCase#getContext or SysuiTestCase#mContext");
+ });
+ when(inst.getTargetContext()).thenAnswer(invocation -> {
+ throw new RuntimeException(
+ "SysUI Tests should use SysuiTestCase#getContext or SysuiTestCase#mContext");
+ });
InstrumentationRegistry.registerInstance(inst, InstrumentationRegistry.getArguments());
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
index 2363b2a..8641fac 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
@@ -27,7 +27,6 @@
import android.os.Handler;
import android.os.Looper;
import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.FlakyTest;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/plugins/PluginManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/plugins/PluginManagerTest.java
index b8e9fcd..bba982c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/plugins/PluginManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/plugins/PluginManagerTest.java
@@ -26,8 +26,6 @@
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
-import android.support.test.annotation.UiThreadTest;
-import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
@@ -36,11 +34,10 @@
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
import com.android.systemui.Dependency;
import com.android.systemui.SysuiTestCase;
-import com.android.systemui.plugins.annotations.ProvidesInterface;
import com.android.systemui.plugins.PluginInstanceManager.PluginInfo;
+import com.android.systemui.plugins.annotations.ProvidesInterface;
import com.android.systemui.plugins.PluginManagerImpl.PluginInstanceManagerFactory;
-import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSDetailTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSDetailTest.java
index 6a85511..ed47fbb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSDetailTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSDetailTest.java
@@ -23,10 +23,7 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import org.junit.After;
-import org.junit.Ignore;
import android.support.test.filters.SmallTest;
-import android.support.test.filters.FlakyTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
@@ -41,6 +38,7 @@
import com.android.systemui.plugins.qs.DetailAdapter;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterTest.java
index f4fda06..d25bbe1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterTest.java
@@ -21,7 +21,6 @@
import static org.mockito.Mockito.when;
import android.support.test.filters.SmallTest;
-import android.support.test.filters.FlakyTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java
index 637b244..85cdfcc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.java
@@ -20,7 +20,6 @@
import static org.mockito.Mockito.when;
import android.support.test.filters.SmallTest;
-import android.support.test.filters.FlakyTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
@@ -31,7 +30,6 @@
import com.android.systemui.qs.customize.QSCustomizer;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -40,7 +38,6 @@
@RunWith(AndroidTestingRunner.class)
@RunWithLooper
@SmallTest
-@FlakyTest
public class QSPanelTest extends SysuiTestCase {
private MetricsLogger mMetricsLogger;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/ExpandableNotificationRowTest.java
index 664ea71..2f6511c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/ExpandableNotificationRowTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/ExpandableNotificationRowTest.java
@@ -21,27 +21,21 @@
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
-import android.content.Context;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.annotation.UiThreadTest;
-import android.support.test.filters.FlakyTest;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
import android.view.View;
+import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.notification.AboveShelfChangedListener;
import com.android.systemui.statusbar.stack.NotificationChildrenContainer;
-import com.android.systemui.SysuiTestCase;
import org.junit.Assert;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
@SmallTest
@RunWith(AndroidJUnit4.class)
-@FlakyTest
public class ExpandableNotificationRowTest extends SysuiTestCase {
private ExpandableNotificationRow mGroup;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationContentViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationContentViewTest.java
index 4dce2f5..436849c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationContentViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationContentViewTest.java
@@ -16,30 +16,25 @@
package com.android.systemui.statusbar;
-import android.content.Context;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.annotation.UiThreadTest;
-import android.support.test.filters.FlakyTest;
-import android.support.test.filters.SmallTest;
-import android.support.test.runner.AndroidJUnit4;
-import android.view.View;
-
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.Ignore;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.spy;
+import android.support.test.annotation.UiThreadTest;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.view.View;
+
import com.android.systemui.SysuiTestCase;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
@SmallTest
@RunWith(AndroidJUnit4.class)
-@FlakyTest
public class NotificationContentViewTest extends SysuiTestCase {
NotificationContentView mView;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationCustomViewWrapperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationCustomViewWrapperTest.java
index d18e63b..6e59d10 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationCustomViewWrapperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationCustomViewWrapperTest.java
@@ -16,10 +16,7 @@
package com.android.systemui.statusbar;
-import android.content.Context;
-import android.support.test.InstrumentationRegistry;
import android.support.test.annotation.UiThreadTest;
-import android.support.test.filters.FlakyTest;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
import android.view.View;
@@ -32,13 +29,11 @@
import org.junit.Assert;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
@SmallTest
@RunWith(AndroidJUnit4.class)
-@FlakyTest
public class NotificationCustomViewWrapperTest extends SysuiTestCase {
private ExpandableNotificationRow mRow;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/stack/NotificationChildrenContainerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/stack/NotificationChildrenContainerTest.java
index bc6833d..5ac965c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/stack/NotificationChildrenContainerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/stack/NotificationChildrenContainerTest.java
@@ -16,10 +16,6 @@
package com.android.systemui.statusbar.stack;
-import android.content.Context;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.annotation.UiThreadTest;
-import android.support.test.filters.FlakyTest;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
import android.view.NotificationHeaderView;
@@ -31,13 +27,11 @@
import org.junit.Assert;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
@SmallTest
@RunWith(AndroidJUnit4.class)
-@FlakyTest
public class NotificationChildrenContainerTest extends SysuiTestCase {
private ExpandableNotificationRow mGroup;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakePluginManager.java b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakePluginManager.java
index 0a83a89..d1b1c5b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakePluginManager.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/utils/leaks/FakePluginManager.java
@@ -14,7 +14,6 @@
package com.android.systemui.utils.leaks;
-import android.content.Context;
import android.testing.LeakCheck;
import com.android.systemui.plugins.Plugin;
diff --git a/proto/src/metrics_constants.proto b/proto/src/metrics_constants.proto
index 2edcd71..9aa58f5 100644
--- a/proto/src/metrics_constants.proto
+++ b/proto/src/metrics_constants.proto
@@ -4153,11 +4153,44 @@
// OS: O DR
FIELD_PLUG_BATTERY_PERCENTAGE = 1024;
+ // Device Headset battery level on Plug
+ // CATEGORY: OTHER
+ // FIELD - The battery percentage when the user decided to plug in
+ // Type: integer
+ // OS: O DR
+ FIELD_UNPLUG_BATTERY_PERCENTAGE = 1025;
+
// Device Headset Pose status
// CATEGORY: OTHER
// SUBTYPE: 1 is 6DOF, 2 is 3DOF
// OS: O DR
- ACTION_HEADSET_POSE_STATUS = 1025;
+ ACTION_HEADSET_POSE_STATUS = 1026;
+
+ // Device Headset Usage session time
+ // CATEGORY: OTHER
+ // FIELD - The time the headset was used in a session
+ // OS: O DR
+ FIELD_SESSION_TIME_MS = 1027;
+
+ // Device Headset Idle time
+ // CATEGORY: OTHER
+ // FIELD - The time in between each session
+ // OS: O DR
+ FIELD_TIME_ELAPSED_BETWEEN_SESSION_MS = 1028;
+
+ // Device Headset charge session time
+ // CATEGORY: OTHER
+ // FIELD - The time taken for each charge
+ // OS: O DR
+ FIELD_TIME_OF_CHARGE_MS = 1029;
+
+ // Device Headset time between charge
+ // CATEGORY: OTHER
+ // FIELD - The time in between each charge
+ // OS: O DR
+ FIELD_TIME_ELAPSED_BETWEEN_CHARGE_MS = 1030;
+
+ // ---- End O-DR1 Constants, all O-DR1 constants go above this line ----
// Add new aosp constants above this line.
// END OF AOSP CONSTANTS
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
index 9e4d89c..0e42e6d 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityInputFilter.java
@@ -271,6 +271,7 @@
private void processKeyEvent(EventStreamState state, KeyEvent event, int policyFlags) {
if (!state.shouldProcessKeyEvent(event)) {
+ super.onInputEvent(event, policyFlags);
return;
}
mEventHandler.onKeyEvent(event, policyFlags);
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index 4810f4f..f47b0d3 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -383,7 +383,7 @@
findDeviceCallback,
getServiceCallback());
} catch (RemoteException e) {
- throw new RuntimeException(e);
+ Log.e(LOG_TAG, "Error while initiating device discovery", e);
}
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index fd4f672..f106ca0 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -20,6 +20,7 @@
import static android.Manifest.permission.CHANGE_DEVICE_IDLE_TEMP_WHITELIST;
import static android.Manifest.permission.INTERACT_ACROSS_USERS;
import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
+import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW;
import static android.Manifest.permission.MANAGE_ACTIVITY_STACKS;
import static android.Manifest.permission.READ_FRAME_BUFFER;
import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
@@ -10297,7 +10298,7 @@
@Override
public void moveStackToDisplay(int stackId, int displayId) {
- enforceCallingPermission(MANAGE_ACTIVITY_STACKS, "moveStackToDisplay()");
+ enforceCallingPermission(INTERNAL_SYSTEM_WINDOW, "moveStackToDisplay()");
synchronized (this) {
final long ident = Binder.clearCallingIdentity();
@@ -12573,7 +12574,6 @@
Binder.restoreCallingIdentity(ident);
}
}
- closeSystemDialogs("setLockScreenShown");
}
@Override
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 1ccac1b..e0f2a75 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -16,6 +16,7 @@
package com.android.server.am;
+import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW;
import static android.Manifest.permission.MANAGE_ACTIVITY_STACKS;
import static android.Manifest.permission.START_ANY_ACTIVITY;
import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
@@ -38,11 +39,13 @@
import static android.content.Intent.FLAG_ACTIVITY_MULTIPLE_TASK;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.os.Process.SYSTEM_UID;
import static android.os.Trace.TRACE_TAG_ACTIVITY_MANAGER;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.FLAG_PRIVATE;
import static android.view.Display.INVALID_DISPLAY;
import static android.view.Display.REMOVE_MODE_DESTROY_CONTENT;
+import static android.view.Display.TYPE_VIRTUAL;
import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.ACTION_PICTURE_IN_PICTURE_EXPANDED_TO_FULLSCREEN;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_ALL;
@@ -1694,6 +1697,24 @@
return false;
}
+ // Check if the caller can manage activity stacks.
+ final int startAnyPerm = mService.checkPermission(INTERNAL_SYSTEM_WINDOW, callingPid,
+ callingUid);
+ if (startAnyPerm == PERMISSION_GRANTED) {
+ if (DEBUG_TASKS) Slog.d(TAG, "Launch on display check:"
+ + " allow launch any on display");
+ return true;
+ }
+
+ if (activityDisplay.mDisplay.getType() == TYPE_VIRTUAL
+ && activityDisplay.mDisplay.getOwnerUid() != SYSTEM_UID) {
+ // Limit launching on virtual displays, because their contents can be read from Surface
+ // by apps that created them.
+ if (DEBUG_TASKS) Slog.d(TAG, "Launch on display check:"
+ + " disallow launch on virtual display for not-embedded activity");
+ return false;
+ }
+
if (!activityDisplay.isPrivate()) {
// Anyone can launch on a public display.
if (DEBUG_TASKS) Slog.d(TAG, "Launch on display check:"
@@ -1715,15 +1736,6 @@
return true;
}
- // Check if the caller can manage activity stacks.
- final int startAnyPerm = mService.checkPermission(MANAGE_ACTIVITY_STACKS, callingPid,
- callingUid);
- if (startAnyPerm == PERMISSION_GRANTED) {
- if (DEBUG_TASKS) Slog.d(TAG, "Launch on display check:"
- + " allow launch any on display");
- return true;
- }
-
Slog.w(TAG, "Launch on display check: denied");
return false;
}
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index e5ab784..2199bba 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -239,6 +239,7 @@
private static final int MSG_SET_A2DP_SRC_CONNECTION_STATE = 101;
private static final int MSG_SET_A2DP_SINK_CONNECTION_STATE = 102;
private static final int MSG_A2DP_DEVICE_CONFIG_CHANGE = 103;
+ private static final int MSG_DISABLE_AUDIO_FOR_UID = 104;
// end of messages handled under wakelock
private static final int BTA2DP_DOCK_TIMEOUT_MILLIS = 8000;
@@ -4871,6 +4872,12 @@
mAudioEventWakeLock.release();
break;
+ case MSG_DISABLE_AUDIO_FOR_UID:
+ mPlaybackMonitor.disableAudioForUid( msg.arg1 == 1 /* disable */,
+ msg.arg2 /* uid */);
+ mAudioEventWakeLock.release();
+ break;
+
case MSG_REPORT_NEW_ROUTES: {
int N = mRoutesObservers.beginBroadcast();
if (N > 0) {
@@ -6591,6 +6598,13 @@
}
}
}
+
+ @Override
+ public void disableAudioForUid(boolean disable, int uid) {
+ queueMsgUnderWakeLock(mAudioHandler, MSG_DISABLE_AUDIO_FOR_UID,
+ disable ? 1 : 0 /* arg1 */, uid /* arg2 */,
+ null /* obj */, 0 /* delay */);
+ }
}
//==========================================================================================
diff --git a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
index f9e4d94..663559f 100644
--- a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
+++ b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
@@ -95,6 +95,43 @@
}
//=================================================================
+ private final ArrayList<Integer> mBannedUids = new ArrayList<Integer>();
+
+ // see AudioManagerInternal.disableAudioForUid(boolean disable, int uid)
+ public void disableAudioForUid(boolean disable, int uid) {
+ synchronized(mPlayerLock) {
+ final int index = mBannedUids.indexOf(new Integer(uid));
+ if (index >= 0) {
+ if (!disable) {
+ mBannedUids.remove(index);
+ // nothing else to do, future playback requests from this uid are ok
+ } // no else to handle, uid already present, so disabling again is no-op
+ } else {
+ if (disable) {
+ for (AudioPlaybackConfiguration apc : mPlayers.values()) {
+ checkBanPlayer(apc, uid);
+ }
+ mBannedUids.add(new Integer(uid));
+ } // no else to handle, uid already not in list, so enabling again is no-op
+ }
+ }
+ }
+
+ private boolean checkBanPlayer(@NonNull AudioPlaybackConfiguration apc, int uid) {
+ final boolean toBan = (apc.getClientUid() == uid);
+ if (toBan) {
+ final int piid = apc.getPlayerInterfaceId();
+ try {
+ Log.v(TAG, "banning player " + piid + " uid:" + uid);
+ apc.getPlayerProxy().pause();
+ } catch (Exception e) {
+ Log.e(TAG, "error banning player " + piid + " uid:" + uid, e);
+ }
+ }
+ return toBan;
+ }
+
+ //=================================================================
// Track players and their states
// methods playerAttributes, playerEvent, releasePlayer are all oneway calls
// into AudioService. They trigger synchronous dispatchPlaybackChange() which updates
@@ -137,6 +174,14 @@
if (apc == null) {
return;
}
+ if (event == AudioPlaybackConfiguration.PLAYER_STATE_STARTED) {
+ for (Integer uidInteger: mBannedUids) {
+ if (checkBanPlayer(apc, uidInteger.intValue())) {
+ // player was banned, do not update its state
+ return;
+ }
+ }
+ }
if (apc.getPlayerType() == AudioPlaybackConfiguration.PLAYER_TYPE_JAM_SOUNDPOOL) {
// FIXME SoundPool not ready for state reporting
return;
@@ -186,10 +231,17 @@
pw.println("\n ducked players:");
mDuckingManager.dump(pw);
// players muted due to the device ringing or being in a call
- pw.println("\n muted player piids:");
+ pw.print("\n muted player piids:");
for (int piid : mMutedPlayers) {
- pw.println(" " + piid);
+ pw.print(" " + piid);
}
+ pw.println();
+ // banned players:
+ pw.print("\n banned uids:");
+ for (int uid : mBannedUids) {
+ pw.print(" " + uid);
+ }
+ pw.println();
}
}
diff --git a/services/core/java/com/android/server/fingerprint/AuthenticationClient.java b/services/core/java/com/android/server/fingerprint/AuthenticationClient.java
index 5339bac..370e569 100644
--- a/services/core/java/com/android/server/fingerprint/AuthenticationClient.java
+++ b/services/core/java/com/android/server/fingerprint/AuthenticationClient.java
@@ -79,7 +79,7 @@
}
if (!authenticated) {
if (receiver != null) {
- FingerprintUtils.vibrateFingerprintError(getContext());
+ vibrateError();
}
// allow system-defined limit of number of attempts before giving up
int lockoutMode = handleFailedAttempt();
@@ -99,7 +99,7 @@
result |= lockoutMode != LOCKOUT_NONE; // in a lockout mode
} else {
if (receiver != null) {
- FingerprintUtils.vibrateFingerprintSuccess(getContext());
+ vibrateSuccess();
}
result |= true; // we have a valid fingerprint, done
resetFailedAttempts();
diff --git a/services/core/java/com/android/server/fingerprint/ClientMonitor.java b/services/core/java/com/android/server/fingerprint/ClientMonitor.java
index 1a2e144..3eae157 100644
--- a/services/core/java/com/android/server/fingerprint/ClientMonitor.java
+++ b/services/core/java/com/android/server/fingerprint/ClientMonitor.java
@@ -23,6 +23,8 @@
import android.hardware.fingerprint.IFingerprintServiceReceiver;
import android.os.IBinder;
import android.os.RemoteException;
+import android.os.VibrationEffect;
+import android.os.Vibrator;
import android.util.Slog;
import java.util.NoSuchElementException;
@@ -36,14 +38,18 @@
protected static final String TAG = FingerprintService.TAG; // TODO: get specific name
protected static final int ERROR_ESRCH = 3; // Likely fingerprint HAL is dead. See errno.h.
protected static final boolean DEBUG = FingerprintService.DEBUG;
+ private static final long[] DEFAULT_SUCCESS_VIBRATION_PATTERN = new long[] {0, 30};
+ private final Context mContext;
+ private final long mHalDeviceId;
+ private final int mTargetUserId;
+ private final int mGroupId;
+ // True if client does not have MANAGE_FINGERPRINT permission
+ private final boolean mIsRestricted;
+ private final String mOwner;
+ private final VibrationEffect mSuccessVibrationEffect;
+ private final VibrationEffect mErrorVibrationEffect;
private IBinder mToken;
private IFingerprintServiceReceiver mReceiver;
- private int mTargetUserId;
- private int mGroupId;
- private boolean mIsRestricted; // True if client does not have MANAGE_FINGERPRINT permission
- private String mOwner;
- private Context mContext;
- private long mHalDeviceId;
protected boolean mAlreadyCancelled;
/**
@@ -68,6 +74,8 @@
mGroupId = groupId;
mIsRestricted = restricted;
mOwner = owner;
+ mSuccessVibrationEffect = getSuccessVibrationEffect(context);
+ mErrorVibrationEffect = VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK);
try {
if (token != null) {
token.linkToDeath(this, 0);
@@ -79,7 +87,7 @@
/**
* Contacts fingerprint HAL to start the client.
- * @return 0 on succes, errno from driver on failure
+ * @return 0 on success, errno from driver on failure
*/
public abstract int start();
@@ -211,4 +219,39 @@
public final IBinder getToken() {
return mToken;
}
+
+ public final void vibrateSuccess() {
+ Vibrator vibrator = mContext.getSystemService(Vibrator.class);
+ if (vibrator != null) {
+ vibrator.vibrate(mSuccessVibrationEffect);
+ }
+ }
+
+ public final void vibrateError() {
+ Vibrator vibrator = mContext.getSystemService(Vibrator.class);
+ if (vibrator != null) {
+ vibrator.vibrate(mErrorVibrationEffect);
+ }
+ }
+
+ private static VibrationEffect getSuccessVibrationEffect(Context ctx) {
+ int[] arr = ctx.getResources().getIntArray(
+ com.android.internal.R.array.config_longPressVibePattern);
+ final long[] vibePattern;
+ if (arr == null || arr.length == 0) {
+ vibePattern = DEFAULT_SUCCESS_VIBRATION_PATTERN;
+ } else {
+ vibePattern = new long[arr.length];
+ for (int i = 0; i < arr.length; i++) {
+ vibePattern[i] = arr[i];
+ }
+ }
+ if (vibePattern.length == 1) {
+ return VibrationEffect.createOneShot(
+ vibePattern[0], VibrationEffect.DEFAULT_AMPLITUDE);
+ } else {
+ return VibrationEffect.createWaveform(vibePattern, -1);
+ }
+ }
+
}
diff --git a/services/core/java/com/android/server/fingerprint/EnrollClient.java b/services/core/java/com/android/server/fingerprint/EnrollClient.java
index 6170894..c9efcf2 100644
--- a/services/core/java/com/android/server/fingerprint/EnrollClient.java
+++ b/services/core/java/com/android/server/fingerprint/EnrollClient.java
@@ -65,7 +65,7 @@
if (receiver == null)
return true; // client not listening
- FingerprintUtils.vibrateFingerprintSuccess(getContext());
+ vibrateSuccess();
MetricsLogger.action(getContext(), MetricsEvent.ACTION_FINGERPRINT_ENROLL);
try {
receiver.onEnrollResult(getHalDeviceId(), fpId, groupId, remaining);
diff --git a/services/core/java/com/android/server/fingerprint/FingerprintUtils.java b/services/core/java/com/android/server/fingerprint/FingerprintUtils.java
index 49dc8e4..5fbd735 100644
--- a/services/core/java/com/android/server/fingerprint/FingerprintUtils.java
+++ b/services/core/java/com/android/server/fingerprint/FingerprintUtils.java
@@ -18,7 +18,6 @@
import android.content.Context;
import android.hardware.fingerprint.Fingerprint;
-import android.os.Vibrator;
import android.text.TextUtils;
import android.util.SparseArray;
@@ -31,9 +30,6 @@
*/
public class FingerprintUtils {
- private static final long[] FP_ERROR_VIBRATE_PATTERN = new long[] {0, 30, 100, 30};
- private static final long[] FP_SUCCESS_VIBRATE_PATTERN = new long[] {0, 30};
-
private static final Object sInstanceLock = new Object();
private static FingerprintUtils sInstance;
@@ -72,20 +68,6 @@
getStateForUser(ctx, userId).renameFingerprint(fingerId, name);
}
- public static void vibrateFingerprintError(Context context) {
- Vibrator vibrator = context.getSystemService(Vibrator.class);
- if (vibrator != null) {
- vibrator.vibrate(FP_ERROR_VIBRATE_PATTERN, -1);
- }
- }
-
- public static void vibrateFingerprintSuccess(Context context) {
- Vibrator vibrator = context.getSystemService(Vibrator.class);
- if (vibrator != null) {
- vibrator.vibrate(FP_SUCCESS_VIBRATE_PATTERN, -1);
- }
- }
-
private FingerprintsUserState getStateForUser(Context ctx, int userId) {
synchronized (this) {
FingerprintsUserState state = mUsers.get(userId);
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 5ee7ac4..038e47e 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -4004,16 +4004,19 @@
}
int indexBefore = findNotificationRecordIndexLocked(record);
boolean interceptBefore = record.isIntercepted();
+ float contactAffinityBefore = record.getContactAffinity();
int visibilityBefore = record.getPackageVisibilityOverride();
recon.applyChangesLocked(record);
applyZenModeLocked(record);
mRankingHelper.sort(mNotificationList);
int indexAfter = findNotificationRecordIndexLocked(record);
boolean interceptAfter = record.isIntercepted();
+ float contactAffinityAfter = record.getContactAffinity();
int visibilityAfter = record.getPackageVisibilityOverride();
changed = indexBefore != indexAfter || interceptBefore != interceptAfter
|| visibilityBefore != visibilityAfter;
- if (interceptBefore && !interceptAfter) {
+ if (interceptBefore && !interceptAfter
+ && Float.compare(contactAffinityBefore, contactAffinityAfter) != 0) {
buzzBeepBlinkLocked(record);
}
}
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 89dbc2a..5d2d4b6 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -1677,8 +1677,17 @@
}
boolean isUserSetupComplete() {
- return Settings.Secure.getIntForUser(mContext.getContentResolver(),
+ boolean isSetupComplete = Settings.Secure.getIntForUser(mContext.getContentResolver(),
Settings.Secure.USER_SETUP_COMPLETE, 0, UserHandle.USER_CURRENT) != 0;
+ if (mHasFeatureLeanback) {
+ isSetupComplete &= isTvUserSetupComplete();
+ }
+ return isSetupComplete;
+ }
+
+ private boolean isTvUserSetupComplete() {
+ return Settings.Secure.getIntForUser(mContext.getContentResolver(),
+ Settings.Secure.TV_USER_SETUP_COMPLETE, 0, UserHandle.USER_CURRENT) != 0;
}
private void handleShortPressOnHome() {
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 32871bb..984b40f 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -783,7 +783,7 @@
mHandler.post(() -> {
// ShutdownThread displays UI, so give it a UI context.
if (safeMode) {
- ShutdownThread.rebootSafeMode(getUiContext(), false);
+ ShutdownThread.rebootSafeMode(getUiContext(), true);
} else {
ShutdownThread.reboot(getUiContext(),
PowerManager.SHUTDOWN_USER_REQUESTED, false);
diff --git a/services/core/java/com/android/server/wm/AppWindowContainerController.java b/services/core/java/com/android/server/wm/AppWindowContainerController.java
index 5f34c60..4e4398e 100644
--- a/services/core/java/com/android/server/wm/AppWindowContainerController.java
+++ b/services/core/java/com/android/server/wm/AppWindowContainerController.java
@@ -614,7 +614,7 @@
return STARTING_WINDOW_TYPE_SPLASH_SCREEN;
} else if (taskSwitch && allowTaskSnapshot) {
return snapshot == null ? STARTING_WINDOW_TYPE_NONE
- : snapshotOrientationSameAsDisplay(snapshot) || fromRecents
+ : snapshotOrientationSameAsTask(snapshot) || fromRecents
? STARTING_WINDOW_TYPE_SNAPSHOT : STARTING_WINDOW_TYPE_SPLASH_SCREEN;
} else {
return STARTING_WINDOW_TYPE_NONE;
@@ -640,24 +640,11 @@
return true;
}
- private boolean snapshotOrientationSameAsDisplay(TaskSnapshot snapshot) {
+ private boolean snapshotOrientationSameAsTask(TaskSnapshot snapshot) {
if (snapshot == null) {
return false;
}
- final Rect rect = new Rect(0, 0, snapshot.getSnapshot().getWidth(),
- snapshot.getSnapshot().getHeight());
- rect.inset(snapshot.getContentInsets());
- final Rect taskBoundsWithoutInsets = new Rect();
- mContainer.getTask().getBounds(taskBoundsWithoutInsets);
- final DisplayInfo di = mContainer.getDisplayContent().getDisplayInfo();
- final Rect displayBounds = new Rect(0, 0, di.logicalWidth, di.logicalHeight);
- final Rect stableInsets = new Rect();
- mService.mPolicy.getStableInsetsLw(di.rotation, di.logicalWidth, di.logicalHeight,
- stableInsets);
- displayBounds.inset(stableInsets);
- final boolean snapshotInLandscape = rect.width() >= rect.height();
- final boolean displayInLandscape = displayBounds.width() >= displayBounds.height();
- return snapshotInLandscape == displayInLandscape;
+ return mContainer.getTask().getConfiguration().orientation == snapshot.getOrientation();
}
public void removeStartingWindow() {
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index bd37934..8afc4fd 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -937,8 +937,6 @@
// Update keyguard flags upon finishing relaunch.
checkKeyguardFlagsChanged();
}
-
- updateAllDrawn();
}
void clearRelaunching() {
@@ -1344,6 +1342,10 @@
}
}
+ /**
+ * Determines if the token has finished drawing. This should only be called from
+ * {@link DisplayContent#applySurfaceChangesTransaction}
+ */
void updateAllDrawn() {
if (!allDrawn) {
// Number of drawn windows can be less when a window is being relaunched, wait for
diff --git a/services/core/jni/com_android_server_VibratorService.cpp b/services/core/jni/com_android_server_VibratorService.cpp
index 804cd17..cb8416b 100644
--- a/services/core/jni/com_android_server_VibratorService.cpp
+++ b/services/core/jni/com_android_server_VibratorService.cpp
@@ -42,107 +42,121 @@
namespace android
{
-static sp<IVibrator> mHal;
+static constexpr int NUM_TRIES = 2;
+
+// Creates a Return<R> with STATUS::EX_NULL_POINTER.
+template<class R>
+inline Return<R> NullptrStatus() {
+ using ::android::hardware::Status;
+ return Return<R>{Status::fromExceptionCode(Status::EX_NULL_POINTER)};
+}
+
+// Helper used to transparently deal with the vibrator HAL becoming unavailable.
+template<class R, class I, class... Args0, class... Args1>
+Return<R> halCall(Return<R> (I::* fn)(Args0...), Args1&&... args1) {
+ // Assume that if getService returns a nullptr, HAL is not available on the
+ // device.
+ static sp<I> sHal = I::getService();
+ static bool sAvailable = sHal != nullptr;
+
+ if (!sAvailable) {
+ return NullptrStatus<R>();
+ }
+
+ // Return<R> doesn't have a default constructor, so make a Return<R> with
+ // STATUS::EX_NONE.
+ using ::android::hardware::Status;
+ Return<R> ret{Status::fromExceptionCode(Status::EX_NONE)};
+
+ // Note that ret is guaranteed to be changed after this loop.
+ for (int i = 0; i < NUM_TRIES; ++i) {
+ ret = (sHal == nullptr) ? NullptrStatus<R>()
+ : (*sHal.*fn)(std::forward<Args1>(args1)...);
+
+ if (!ret.isOk()) {
+ ALOGE("Failed to issue command to vibrator HAL. Retrying.");
+ // Restoring connection to the HAL.
+ sHal = I::tryGetService();
+ }
+ }
+ return ret;
+}
static void vibratorInit(JNIEnv /* env */, jobject /* clazz */)
{
- /* TODO(b/31632518) */
- if (mHal != nullptr) {
- return;
- }
- mHal = IVibrator::getService();
+ halCall(&IVibrator::ping).isOk();
}
static jboolean vibratorExists(JNIEnv* /* env */, jobject /* clazz */)
{
- if (mHal != nullptr) {
- return JNI_TRUE;
- } else {
- return JNI_FALSE;
- }
+ return halCall(&IVibrator::ping).isOk() ? JNI_TRUE : JNI_FALSE;
}
static void vibratorOn(JNIEnv* /* env */, jobject /* clazz */, jlong timeout_ms)
{
- if (mHal != nullptr) {
- Status retStatus = mHal->on(timeout_ms);
- if (retStatus != Status::OK) {
- ALOGE("vibratorOn command failed (%" PRIu32 ").", static_cast<uint32_t>(retStatus));
- }
- } else {
- ALOGW("Tried to vibrate but there is no vibrator device.");
+ Status retStatus = halCall(&IVibrator::on, timeout_ms).withDefault(Status::UNKNOWN_ERROR);
+ if (retStatus != Status::OK) {
+ ALOGE("vibratorOn command failed (%" PRIu32 ").", static_cast<uint32_t>(retStatus));
}
}
static void vibratorOff(JNIEnv* /* env */, jobject /* clazz */)
{
- if (mHal != nullptr) {
- Status retStatus = mHal->off();
- if (retStatus != Status::OK) {
- ALOGE("vibratorOff command failed (%" PRIu32 ").", static_cast<uint32_t>(retStatus));
- }
- } else {
- ALOGW("Tried to stop vibrating but there is no vibrator device.");
+ Status retStatus = halCall(&IVibrator::off).withDefault(Status::UNKNOWN_ERROR);
+ if (retStatus != Status::OK) {
+ ALOGE("vibratorOff command failed (%" PRIu32 ").", static_cast<uint32_t>(retStatus));
}
}
static jlong vibratorSupportsAmplitudeControl(JNIEnv*, jobject) {
- if (mHal != nullptr) {
- return mHal->supportsAmplitudeControl();
- } else {
- ALOGW("Unable to get max vibration amplitude, there is no vibrator device.");
- }
- return false;
+ return halCall(&IVibrator::supportsAmplitudeControl).withDefault(false);
}
static void vibratorSetAmplitude(JNIEnv*, jobject, jint amplitude) {
- if (mHal != nullptr) {
- Status status = mHal->setAmplitude(static_cast<uint32_t>(amplitude));
- if (status != Status::OK) {
- ALOGE("Failed to set vibrator amplitude (%" PRIu32 ").",
- static_cast<uint32_t>(status));
- }
- } else {
- ALOGW("Unable to set vibration amplitude, there is no vibrator device.");
+ Status status = halCall(&IVibrator::setAmplitude, static_cast<uint32_t>(amplitude))
+ .withDefault(Status::UNKNOWN_ERROR);
+ if (status != Status::OK) {
+ ALOGE("Failed to set vibrator amplitude (%" PRIu32 ").",
+ static_cast<uint32_t>(status));
}
}
static jlong vibratorPerformEffect(JNIEnv*, jobject, jlong effect, jint strength) {
- if (mHal != nullptr) {
- Status status;
- uint32_t lengthMs;
- auto callback = [&status, &lengthMs](Status retStatus, uint32_t retLengthMs) {
- status = retStatus;
- lengthMs = retLengthMs;
- };
- EffectStrength effectStrength(static_cast<EffectStrength>(strength));
+ Status status;
+ uint32_t lengthMs;
+ auto callback = [&status, &lengthMs](Status retStatus, uint32_t retLengthMs) {
+ status = retStatus;
+ lengthMs = retLengthMs;
+ };
+ EffectStrength effectStrength(static_cast<EffectStrength>(strength));
- if (effect < 0 || effect > static_cast<uint32_t>(Effect_1_1::TICK)) {
- ALOGW("Unable to perform haptic effect, invalid effect ID (%" PRId32 ")",
+ if (effect < 0 || effect > static_cast<uint32_t>(Effect_1_1::TICK)) {
+ ALOGW("Unable to perform haptic effect, invalid effect ID (%" PRId32 ")",
+ static_cast<int32_t>(effect));
+ } else if (effect == static_cast<uint32_t>(Effect_1_1::TICK)) {
+ auto ret = halCall(&IVibrator_1_1::perform_1_1, static_cast<Effect_1_1>(effect),
+ effectStrength, callback);
+ if (!ret.isOk()) {
+ ALOGW("Failed to perform effect (%" PRId32 "), insufficient HAL version",
static_cast<int32_t>(effect));
- } else if (effect == static_cast<uint32_t>(Effect_1_1::TICK)) {
- sp<IVibrator_1_1> hal_1_1 = IVibrator_1_1::castFrom(mHal);
- if (hal_1_1 != nullptr) {
- hal_1_1->perform_1_1(static_cast<Effect_1_1>(effect), effectStrength, callback);
- } else {
- ALOGW("Failed to perform effect (%" PRId32 "), insufficient HAL version",
- static_cast<int32_t>(effect));
- }
- } else {
- mHal->perform(static_cast<Effect>(effect), effectStrength, callback);
- }
- if (status == Status::OK) {
- return lengthMs;
- } else if (status != Status::UNSUPPORTED_OPERATION) {
- // Don't warn on UNSUPPORTED_OPERATION, that's a normal even and just means the motor
- // doesn't have a pre-defined waveform to perform for it, so we should just fall back
- // to the framework waveforms.
- ALOGE("Failed to perform haptic effect: effect=%" PRId64 ", strength=%" PRId32
- ", error=%" PRIu32 ").", static_cast<int64_t>(effect),
- static_cast<int32_t>(strength), static_cast<uint32_t>(status));
}
} else {
- ALOGW("Unable to perform haptic effect, there is no vibrator device.");
+ auto ret = halCall(&IVibrator::perform, static_cast<Effect>(effect), effectStrength,
+ callback);
+ if (!ret.isOk()) {
+ ALOGW("Failed to perform effect (%" PRId32 ")", static_cast<int32_t>(effect));
+ }
+ }
+
+ if (status == Status::OK) {
+ return lengthMs;
+ } else if (status != Status::UNSUPPORTED_OPERATION) {
+ // Don't warn on UNSUPPORTED_OPERATION, that's a normal even and just means the motor
+ // doesn't have a pre-defined waveform to perform for it, so we should just fall back
+ // to the framework waveforms.
+ ALOGE("Failed to perform haptic effect: effect=%" PRId64 ", strength=%" PRId32
+ ", error=%" PRIu32 ").", static_cast<int64_t>(effect),
+ static_cast<int32_t>(strength), static_cast<uint32_t>(status));
}
return -1;
}
diff --git a/services/tests/notification/AndroidManifest.xml b/services/tests/notification/AndroidManifest.xml
index 99d9c7b..c20020a 100644
--- a/services/tests/notification/AndroidManifest.xml
+++ b/services/tests/notification/AndroidManifest.xml
@@ -31,7 +31,7 @@
</application>
<instrumentation
- android:name="android.support.test.runner.AndroidJUnitRunner"
+ android:name="android.testing.TestableInstrumentation"
android:targetPackage="com.android.frameworks.tests.notification"
android:label="Notification Tests" />
</manifest>
diff --git a/telephony/java/android/telephony/ImsiEncryptionInfo.java b/telephony/java/android/telephony/ImsiEncryptionInfo.java
index ecb9d25..d2680ad 100644
--- a/telephony/java/android/telephony/ImsiEncryptionInfo.java
+++ b/telephony/java/android/telephony/ImsiEncryptionInfo.java
@@ -17,6 +17,7 @@
import android.os.Parcel;
import android.os.Parcelable;
+import java.util.Date;
import android.util.Log;
import java.security.KeyFactory;
@@ -34,7 +35,6 @@
public final class ImsiEncryptionInfo implements Parcelable {
private static final String LOG_TAG = "ImsiEncryptionInfo";
- private static final boolean DBG = false;
private final String mcc;
@@ -42,14 +42,25 @@
private final PublicKey publicKey;
private final String keyIdentifier;
private final int keyType;
+ //Date-Time in UTC when the key will expire.
+ private final Date expirationTime;
public ImsiEncryptionInfo(String mcc, String mnc, int keyType, String keyIdentifier,
- PublicKey publicKey) {
+ byte[] key, Date expirationTime) {
+ this(mcc, mnc, keyType, keyIdentifier, makeKeyObject(key), expirationTime);
+ }
+
+ public ImsiEncryptionInfo(String mcc, String mnc, int keyType, String keyIdentifier,
+ PublicKey publicKey, Date expirationTime) {
+ // todo need to validate that ImsiEncryptionInfo is being created with the correct params.
+ // Including validating that the public key is in "X.509" format. This will be done in
+ // a subsequent CL.
this.mcc = mcc;
this.mnc = mnc;
this.keyType = keyType;
this.publicKey = publicKey;
this.keyIdentifier = keyIdentifier;
+ this.expirationTime = expirationTime;
}
public ImsiEncryptionInfo(Parcel in) {
@@ -61,7 +72,7 @@
mnc = in.readString();
keyIdentifier = in.readString();
keyType = in.readInt();
-
+ expirationTime = new Date(in.readLong());
}
public String getMnc() {
@@ -84,6 +95,10 @@
return this.publicKey;
}
+ public Date getExpirationTime() {
+ return this.expirationTime;
+ }
+
private static PublicKey makeKeyObject(byte[] publicKeyBytes) {
try {
X509EncodedKeySpec pubKeySpec = new X509EncodedKeySpec(publicKeyBytes);
@@ -91,7 +106,7 @@
} catch (InvalidKeySpecException | NoSuchAlgorithmException ex) {
Log.e(LOG_TAG, "Error makeKeyObject: unable to convert into PublicKey", ex);
}
- return null;
+ throw new IllegalArgumentException();
}
/** Implement the Parcelable interface */
@@ -122,6 +137,7 @@
dest.writeString(mnc);
dest.writeString(keyIdentifier);
dest.writeInt(keyType);
+ dest.writeLong(expirationTime.getTime());
}
@Override
@@ -132,6 +148,7 @@
+ "publicKey=" + publicKey
+ ", keyIdentifier=" + keyIdentifier
+ ", keyType=" + keyType
+ + ", expirationTime=" + expirationTime
+ "]";
}
}
diff --git a/telephony/java/android/telephony/TelephonyScanManager.java b/telephony/java/android/telephony/TelephonyScanManager.java
index c905d3a..92a21b6 100644
--- a/telephony/java/android/telephony/TelephonyScanManager.java
+++ b/telephony/java/android/telephony/TelephonyScanManager.java
@@ -20,15 +20,18 @@
import android.content.Context;
import android.os.Binder;
+import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import android.os.Messenger;
+import android.os.Parcelable;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.util.Log;
import android.util.SparseArray;
+import java.util.Arrays;
import java.util.List;
import com.android.internal.telephony.ITelephony;
@@ -42,6 +45,9 @@
private static final String TAG = "TelephonyScanManager";
/** @hide */
+ public static final String SCAN_RESULT_KEY = "scanResult";
+
+ /** @hide */
public static final int CALLBACK_SCAN_RESULTS = 1;
/** @hide */
public static final int CALLBACK_SCAN_ERROR = 2;
@@ -112,7 +118,13 @@
switch (message.what) {
case CALLBACK_SCAN_RESULTS:
try {
- callback.onResults((List<CellInfo>) message.obj);
+ final Bundle b = message.getData();
+ final Parcelable[] parcelables = b.getParcelableArray(SCAN_RESULT_KEY);
+ CellInfo[] ci = new CellInfo[parcelables.length];
+ for (int i = 0; i < parcelables.length; i++) {
+ ci[i] = (CellInfo) parcelables[i];
+ }
+ callback.onResults((List<CellInfo>) Arrays.asList(ci));
} catch (Exception e) {
Rlog.e(TAG, "Exception in networkscan callback onResults", e);
}
diff --git a/telephony/java/android/telephony/euicc/EuiccManager.java b/telephony/java/android/telephony/euicc/EuiccManager.java
index f22a632..8304d84 100644
--- a/telephony/java/android/telephony/euicc/EuiccManager.java
+++ b/telephony/java/android/telephony/euicc/EuiccManager.java
@@ -475,6 +475,36 @@
}
}
+ /**
+ * Ensure that subscriptions will be retained on the next factory reset.
+ *
+ * <p>By default, all subscriptions on the eUICC are erased the first time a device boots (ever
+ * and after factory resets). This ensures that the data is wiped after a factory reset is
+ * performed via fastboot or recovery mode, as these modes do not support the necessary radio
+ * communication needed to wipe the eSIM.
+ *
+ * <p>However, this method may be called right before a factory reset issued via settings when
+ * the user elects to retain subscriptions. Doing so will mark them for retention so that they
+ * are not cleared after the ensuing reset.
+ *
+ * <p>Requires that the calling app has the {@link android.Manifest.permission#MASTER_CLEAR}
+ * permission. This is for internal system use only.
+ *
+ * @param callbackIntent a PendingIntent to launch when the operation completes.
+ * @hide
+ */
+ public void retainSubscriptionsForFactoryReset(PendingIntent callbackIntent) {
+ if (!isEnabled()) {
+ sendUnavailableError(callbackIntent);
+ return;
+ }
+ try {
+ mController.retainSubscriptionsForFactoryReset(callbackIntent);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
private static void sendUnavailableError(PendingIntent callbackIntent) {
try {
callbackIntent.send(EMBEDDED_SUBSCRIPTION_RESULT_ERROR);
diff --git a/telephony/java/android/telephony/ims/ImsServiceProxy.java b/telephony/java/android/telephony/ims/ImsServiceProxy.java
index a75cd86..038e295 100644
--- a/telephony/java/android/telephony/ims/ImsServiceProxy.java
+++ b/telephony/java/android/telephony/ims/ImsServiceProxy.java
@@ -90,11 +90,11 @@
" status: " + status);
if (mSlotId == slotId && feature == mSupportedFeature) {
mFeatureStatusCached = status;
+ if (mStatusCallback != null) {
+ mStatusCallback.notifyStatusChanged();
+ }
}
}
- if (mStatusCallback != null) {
- mStatusCallback.notifyStatusChanged();
- }
}
};
@@ -129,7 +129,9 @@
@Override
public void endSession(int sessionId) throws RemoteException {
synchronized (mLock) {
- checkServiceIsReady();
+ // Only check to make sure the binder connection still exists. This method should
+ // still be able to be called when the state is STATE_NOT_AVAILABLE.
+ checkBinderConnection();
getServiceInterface(mBinder).endSession(mSlotId, mSupportedFeature, sessionId);
}
}
diff --git a/telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl b/telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl
index fa43631..b3fc90d 100644
--- a/telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl
+++ b/telephony/java/com/android/internal/telephony/euicc/IEuiccController.aidl
@@ -40,4 +40,5 @@
oneway void updateSubscriptionNickname(int subscriptionId, String nickname,
in PendingIntent callbackIntent);
oneway void eraseSubscriptions(in PendingIntent callbackIntent);
+ oneway void retainSubscriptionsForFactoryReset(in PendingIntent callbackIntent);
}
\ No newline at end of file
diff --git a/tests/StatusBar/AndroidManifest.xml b/tests/StatusBar/AndroidManifest.xml
index 81442bf..6a082e9 100644
--- a/tests/StatusBar/AndroidManifest.xml
+++ b/tests/StatusBar/AndroidManifest.xml
@@ -6,6 +6,7 @@
<uses-permission android:name="android.permission.EXPAND_STATUS_BAR" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.CHANGE_COMPONENT_ENABLED_STATE" />
+ <uses-permission android:name="android.permission.MANAGE_NOTIFICATIONS" />
<application>
<activity android:name="StatusBarTest" android:label="_StatusBar">
diff --git a/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java b/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
index 5dd42dd..fc68183 100644
--- a/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
+++ b/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
@@ -129,6 +129,42 @@
mNM.notify(7001, n);
}
},
+ new Test("with zen") {
+ public void run()
+ {
+ mNM.setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_ALARMS);
+ Notification n = new Notification.Builder(NotificationTestList.this,
+ "default")
+ .setSmallIcon(R.drawable.icon2)
+ .setContentTitle("Default priority")
+ .build();
+ mNM.notify("default", 7004, n);
+ try {
+ Thread.sleep(8000);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ mNM.setInterruptionFilter(NotificationManager.INTERRUPTION_FILTER_ALL);
+ }
+ },
+ new Test("repeated") {
+ public void run()
+ {
+ for (int i = 0; i < 50; i++) {
+ Notification n = new Notification.Builder(NotificationTestList.this,
+ "default")
+ .setSmallIcon(R.drawable.icon2)
+ .setContentTitle("Default priority")
+ .build();
+ mNM.notify("default", 7004, n);
+ try {
+ Thread.sleep(100);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ },
new Test("Post a group") {
public void run()
{
diff --git a/tests/testables/src/android/testing/TestableInstrumentation.java b/tests/testables/src/android/testing/TestableInstrumentation.java
new file mode 100644
index 0000000..93fed85
--- /dev/null
+++ b/tests/testables/src/android/testing/TestableInstrumentation.java
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2017 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.testing;
+
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.TestLooperManager;
+import android.support.test.runner.AndroidJUnitRunner;
+import android.util.Log;
+
+import java.util.ArrayList;
+
+/**
+ * Wrapper around instrumentation that spins up a TestLooperManager around
+ * the main looper whenever a test is not using it to attempt to stop crashes
+ * from stopping other tests from running.
+ */
+public class TestableInstrumentation extends AndroidJUnitRunner {
+
+ private static final String TAG = "TestableInstrumentation";
+
+ private static final int MAX_CRASHES = 5;
+ private static MainLooperManager sManager;
+
+ @Override
+ public void onCreate(Bundle arguments) {
+ sManager = new MainLooperManager();
+ Log.setWtfHandler((tag, what, system) -> {
+ if (system) {
+ Log.e(TAG, "WTF!!", what);
+ } else {
+ // These normally kill the app, but we don't want that in a test, instead we want
+ // it to throw.
+ throw new RuntimeException(what);
+ }
+ });
+ super.onCreate(arguments);
+ }
+
+ @Override
+ public void finish(int resultCode, Bundle results) {
+ sManager.destroy();
+ super.finish(resultCode, results);
+ }
+
+ public static void acquireMain() {
+ if (sManager != null) {
+ sManager.acquireMain();
+ }
+ }
+
+ public static void releaseMain() {
+ if (sManager != null) {
+ sManager.releaseMain();
+ }
+ }
+
+ public class MainLooperManager implements Runnable {
+
+ private final ArrayList<Throwable> mExceptions = new ArrayList<>();
+ private Message mStopMessage;
+ private final Handler mMainHandler;
+ private TestLooperManager mManager;
+
+ public MainLooperManager() {
+ mMainHandler = new Handler(Looper.getMainLooper());
+ startManaging();
+ }
+
+ @Override
+ public void run() {
+ try {
+ synchronized (this) {
+ // Let the thing starting us know we are up and ready to run.
+ notify();
+ }
+ while (true) {
+ Message m = mManager.next();
+ if (m == mStopMessage) {
+ mManager.recycle(m);
+ return;
+ }
+ try {
+ mManager.execute(m);
+ } catch (Throwable t) {
+ if (!checkStack(t) || (mExceptions.size() == MAX_CRASHES)) {
+ throw t;
+ }
+ mExceptions.add(t);
+ Log.d(TAG, "Ignoring exception to run more tests", t);
+ }
+ mManager.recycle(m);
+ }
+ } finally {
+ mManager.release();
+ synchronized (this) {
+ // Let the caller know we are done managing the main thread.
+ notify();
+ }
+ }
+ }
+
+ private boolean checkStack(Throwable t) {
+ StackTraceElement topStack = t.getStackTrace()[0];
+ String className = topStack.getClassName();
+ if (className.equals(TestLooperManager.class.getName())) {
+ topStack = t.getCause().getStackTrace()[0];
+ className = topStack.getClassName();
+ }
+ // Only interested in blocking exceptions from the app itself, not from android
+ // framework.
+ return !className.startsWith("android.")
+ && !className.startsWith("com.android.internal");
+ }
+
+ public void destroy() {
+ mStopMessage.sendToTarget();
+ if (mExceptions.size() != 0) {
+ throw new RuntimeException("Exception caught during tests", mExceptions.get(0));
+ }
+ }
+
+ public void acquireMain() {
+ synchronized (this) {
+ mStopMessage.sendToTarget();
+ try {
+ wait();
+ } catch (InterruptedException e) {
+ }
+ }
+ }
+
+ public void releaseMain() {
+ startManaging();
+ }
+
+ private void startManaging() {
+ mStopMessage = mMainHandler.obtainMessage();
+ synchronized (this) {
+ mManager = acquireLooperManager(Looper.getMainLooper());
+ // This bit needs to happen on a background thread or it will hang if called
+ // from the same thread we are looking to block.
+ new Thread(() -> {
+ // Post a message to the main handler that will manage executing all future
+ // messages.
+ mMainHandler.post(this);
+ while (!mManager.hasMessages(mMainHandler, null, this));
+ // Lastly run the message that executes this so it can manage the main thread.
+ Message next = mManager.next();
+ // Run through messages until we reach ours.
+ while (next.getCallback() != this) {
+ mManager.execute(next);
+ mManager.recycle(next);
+ next = mManager.next();
+ }
+ mManager.execute(next);
+ }).start();
+ if (Looper.myLooper() != Looper.getMainLooper()) {
+ try {
+ wait();
+ } catch (InterruptedException e) {
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/tests/testables/src/android/testing/TestableLooper.java b/tests/testables/src/android/testing/TestableLooper.java
index f6c3cb3..f1a7092 100644
--- a/tests/testables/src/android/testing/TestableLooper.java
+++ b/tests/testables/src/android/testing/TestableLooper.java
@@ -29,7 +29,6 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
-import java.lang.reflect.Field;
import java.util.Map;
/**
@@ -49,7 +48,7 @@
private TestLooperManager mQueueWrapper;
public TestableLooper(Looper l) throws Exception {
- this(InstrumentationRegistry.getInstrumentation().acquireLooperManager(l), l);
+ this(acquireLooperManager(l), l);
}
private TestableLooper(TestLooperManager wrapper, Looper l) throws Exception {
@@ -78,6 +77,9 @@
*/
public void destroy() throws NoSuchFieldException, IllegalAccessException {
mQueueWrapper.release();
+ if (mLooper == Looper.getMainLooper()) {
+ TestableInstrumentation.releaseMain();
+ }
}
/**
@@ -196,6 +198,13 @@
}
}
+ private static TestLooperManager acquireLooperManager(Looper l) {
+ if (l == Looper.getMainLooper()) {
+ TestableInstrumentation.acquireMain();
+ }
+ return InstrumentationRegistry.getInstrumentation().acquireLooperManager(l);
+ }
+
private static final Map<Object, TestableLooper> sLoopers = new ArrayMap<>();
/**
@@ -247,8 +256,7 @@
}
boolean set = mTestableLooper.mQueueWrapper == null;
if (set) {
- mTestableLooper.mQueueWrapper = InstrumentationRegistry.getInstrumentation()
- .acquireLooperManager(mLooper);
+ mTestableLooper.mQueueWrapper = acquireLooperManager(mLooper);
}
try {
Object[] ret = new Object[1];
@@ -283,6 +291,9 @@
if (set) {
mTestableLooper.mQueueWrapper.release();
mTestableLooper.mQueueWrapper = null;
+ if (mLooper == Looper.getMainLooper()) {
+ TestableInstrumentation.releaseMain();
+ }
}
}
}