Merge "AOD: Skip window animations while dozing" into oc-dr1-dev
diff --git a/api/current.txt b/api/current.txt
index d7f8ecd..d5c9945 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -43935,7 +43935,6 @@
field public static final int KEYBOARD_TAP = 3; // 0x3
field public static final int LONG_PRESS = 0; // 0x0
field public static final int VIRTUAL_KEY = 1; // 0x1
- field public static final int VIRTUAL_KEY_RELEASE = 7; // 0x7
}
public class InflateException extends java.lang.RuntimeException {
diff --git a/api/system-current.txt b/api/system-current.txt
index 34bba38..8dbff37 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -47474,7 +47474,6 @@
field public static final int KEYBOARD_TAP = 3; // 0x3
field public static final int LONG_PRESS = 0; // 0x0
field public static final int VIRTUAL_KEY = 1; // 0x1
- field public static final int VIRTUAL_KEY_RELEASE = 7; // 0x7
}
public class InflateException extends java.lang.RuntimeException {
diff --git a/api/test-current.txt b/api/test-current.txt
index ed9071a..9c1964b 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -44329,7 +44329,6 @@
field public static final int KEYBOARD_TAP = 3; // 0x3
field public static final int LONG_PRESS = 0; // 0x0
field public static final int VIRTUAL_KEY = 1; // 0x1
- field public static final int VIRTUAL_KEY_RELEASE = 7; // 0x7
}
public class InflateException extends java.lang.RuntimeException {
diff --git a/core/java/android/os/ZygoteProcess.java b/core/java/android/os/ZygoteProcess.java
index 0d81bd9..7a13ee8 100644
--- a/core/java/android/os/ZygoteProcess.java
+++ b/core/java/android/os/ZygoteProcess.java
@@ -488,8 +488,8 @@
* Instructs the zygote to pre-load the classes and native libraries at the given paths
* for the specified abi. Not all zygotes support this function.
*/
- public void preloadPackageForAbi(String packagePath, String libsPath, String cacheKey,
- String abi) throws ZygoteStartFailedEx, IOException {
+ public boolean preloadPackageForAbi(String packagePath, String libsPath, String cacheKey,
+ String abi) throws ZygoteStartFailedEx, IOException {
synchronized(mLock) {
ZygoteState state = openZygoteSocketIfNeeded(abi);
state.writer.write("4");
@@ -508,6 +508,8 @@
state.writer.newLine();
state.writer.flush();
+
+ return (state.inputStream.readInt() == 0);
}
}
diff --git a/core/java/android/view/HapticFeedbackConstants.java b/core/java/android/view/HapticFeedbackConstants.java
index 0a73949..71a3f7e 100644
--- a/core/java/android/view/HapticFeedbackConstants.java
+++ b/core/java/android/view/HapticFeedbackConstants.java
@@ -58,6 +58,7 @@
/**
* The user has released a virtual or software keyboard key.
+ * @hide
*/
public static final int VIRTUAL_KEY_RELEASE = 7;
diff --git a/core/java/com/android/internal/app/ChooserActivity.java b/core/java/com/android/internal/app/ChooserActivity.java
index ece4981..54afc95 100644
--- a/core/java/com/android/internal/app/ChooserActivity.java
+++ b/core/java/com/android/internal/app/ChooserActivity.java
@@ -69,7 +69,10 @@
import android.view.animation.Interpolator;
import android.widget.AbsListView;
import android.widget.BaseAdapter;
+import android.widget.LinearLayout;
import android.widget.ListView;
+import android.widget.Space;
+
import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.ResolverActivity.TargetInfo;
@@ -1412,6 +1415,10 @@
} else {
lp.height = v.getMeasuredHeight();
}
+ if (i != (mColumnCount - 1)) {
+ row.addView(new Space(ChooserActivity.this),
+ new LinearLayout.LayoutParams(0, 0, 1));
+ }
}
// Pre-measure so we can scale later.
@@ -1439,13 +1446,34 @@
if (startType == ChooserListAdapter.TARGET_SERVICE) {
holder.row.setBackgroundColor(
getColor(R.color.chooser_service_row_background_color));
+ int nextStartType = mChooserListAdapter.getPositionTargetType(
+ getFirstRowPosition(rowPosition + 1));
+ int serviceSpacing = holder.row.getContext().getResources()
+ .getDimensionPixelSize(R.dimen.chooser_service_spacing);
+ int top = rowPosition == 0 ? serviceSpacing : 0;
+ if (nextStartType != ChooserListAdapter.TARGET_SERVICE) {
+ setVertPadding(holder, top, serviceSpacing);
+ } else {
+ setVertPadding(holder, top, 0);
+ }
} else {
holder.row.setBackgroundColor(Color.TRANSPARENT);
+ int lastStartType = mChooserListAdapter.getPositionTargetType(
+ getFirstRowPosition(rowPosition - 1));
+ if (lastStartType == ChooserListAdapter.TARGET_SERVICE || rowPosition == 0) {
+ int serviceSpacing = holder.row.getContext().getResources()
+ .getDimensionPixelSize(R.dimen.chooser_service_spacing);
+ setVertPadding(holder, serviceSpacing, 0);
+ } else {
+ setVertPadding(holder, 0, 0);
+ }
}
final int oldHeight = holder.row.getLayoutParams().height;
+ int measuredRowHeight = holder.measuredRowHeight + holder.row.getPaddingTop()
+ + holder.row.getPaddingBottom();
holder.row.getLayoutParams().height = Math.max(1,
- (int) (holder.measuredRowHeight * getRowScale(rowPosition)));
+ (int) (measuredRowHeight * getRowScale(rowPosition)));
if (holder.row.getLayoutParams().height != oldHeight) {
holder.row.requestLayout();
}
@@ -1457,11 +1485,16 @@
holder.itemIndices[i] = start + i;
mChooserListAdapter.bindView(holder.itemIndices[i], v);
} else {
- v.setVisibility(View.GONE);
+ v.setVisibility(View.INVISIBLE);
}
}
}
+ private void setVertPadding(RowViewHolder holder, int top, int bottom) {
+ holder.row.setPadding(holder.row.getPaddingLeft(), top,
+ holder.row.getPaddingRight(), bottom);
+ }
+
int getFirstRowPosition(int row) {
final int callerCount = mChooserListAdapter.getCallerTargetCount();
final int callerRows = (int) Math.ceil((float) callerCount / mColumnCount);
diff --git a/core/java/com/android/internal/content/FileSystemProvider.java b/core/java/com/android/internal/content/FileSystemProvider.java
index 79544df..ebea1a4 100644
--- a/core/java/com/android/internal/content/FileSystemProvider.java
+++ b/core/java/com/android/internal/content/FileSystemProvider.java
@@ -27,6 +27,7 @@
import android.database.MatrixCursor.RowBuilder;
import android.graphics.Point;
import android.net.Uri;
+import android.os.Binder;
import android.os.CancellationSignal;
import android.os.FileObserver;
import android.os.FileUtils;
@@ -156,11 +157,17 @@
if (visibleFolder != null) {
assert (visibleFolder.isDirectory());
- final ContentResolver resolver = getContext().getContentResolver();
- final Uri uri = MediaStore.Files.getDirectoryUri("external");
- ContentValues values = new ContentValues();
- values.put(MediaStore.Files.FileColumns.DATA, visibleFolder.getAbsolutePath());
- resolver.insert(uri, values);
+ final long token = Binder.clearCallingIdentity();
+
+ try {
+ final ContentResolver resolver = getContext().getContentResolver();
+ final Uri uri = MediaStore.Files.getDirectoryUri("external");
+ ContentValues values = new ContentValues();
+ values.put(MediaStore.Files.FileColumns.DATA, visibleFolder.getAbsolutePath());
+ resolver.insert(uri, values);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
}
}
@@ -214,22 +221,28 @@
// They should be all null or not null at the same time. File#renameTo() doesn't work across
// volumes so an exception will be thrown before calling this method.
if (oldVisibleFile != null && newVisibleFile != null) {
- final ContentResolver resolver = getContext().getContentResolver();
- final Uri externalUri = newVisibleFile.isDirectory()
- ? MediaStore.Files.getDirectoryUri("external")
- : MediaStore.Files.getContentUri("external");
+ final long token = Binder.clearCallingIdentity();
- ContentValues values = new ContentValues();
- values.put(MediaStore.Files.FileColumns.DATA, newVisibleFile.getAbsolutePath());
+ try {
+ final ContentResolver resolver = getContext().getContentResolver();
+ final Uri externalUri = newVisibleFile.isDirectory()
+ ? MediaStore.Files.getDirectoryUri("external")
+ : MediaStore.Files.getContentUri("external");
- // Logic borrowed from MtpDatabase.
- // note - we are relying on a special case in MediaProvider.update() to update
- // the paths for all children in the case where this is a directory.
- final String path = oldVisibleFile.getAbsolutePath();
- resolver.update(externalUri,
- values,
- "_data LIKE ? AND lower(_data)=lower(?)",
- new String[] { path, path });
+ ContentValues values = new ContentValues();
+ values.put(MediaStore.Files.FileColumns.DATA, newVisibleFile.getAbsolutePath());
+
+ // Logic borrowed from MtpDatabase.
+ // note - we are relying on a special case in MediaProvider.update() to update
+ // the paths for all children in the case where this is a directory.
+ final String path = oldVisibleFile.getAbsolutePath();
+ resolver.update(externalUri,
+ values,
+ "_data LIKE ? AND lower(_data)=lower(?)",
+ new String[]{path, path});
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
}
}
@@ -253,23 +266,29 @@
throws FileNotFoundException {
// visibleFolder is null if we're removing a document from external thumb drive or SD card.
if (visibleFile != null) {
- final ContentResolver resolver = getContext().getContentResolver();
- final Uri externalUri = MediaStore.Files.getContentUri("external");
+ final long token = Binder.clearCallingIdentity();
- // Remove media store entries for any files inside this directory, using
- // path prefix match. Logic borrowed from MtpDatabase.
- if (isFolder) {
- final String path = visibleFile.getAbsolutePath() + "/";
+ try {
+ final ContentResolver resolver = getContext().getContentResolver();
+ final Uri externalUri = MediaStore.Files.getContentUri("external");
+
+ // Remove media store entries for any files inside this directory, using
+ // path prefix match. Logic borrowed from MtpDatabase.
+ if (isFolder) {
+ final String path = visibleFile.getAbsolutePath() + "/";
+ resolver.delete(externalUri,
+ "_data LIKE ?1 AND lower(substr(_data,1,?2))=lower(?3)",
+ new String[]{path + "%", Integer.toString(path.length()), path});
+ }
+
+ // Remove media store entry for this exact file.
+ final String path = visibleFile.getAbsolutePath();
resolver.delete(externalUri,
- "_data LIKE ?1 AND lower(substr(_data,1,?2))=lower(?3)",
- new String[] { path + "%", Integer.toString(path.length()), path });
+ "_data LIKE ?1 AND lower(_data)=lower(?2)",
+ new String[]{path, path});
+ } finally {
+ Binder.restoreCallingIdentity(token);
}
-
- // Remove media store entry for this exact file.
- final String path = visibleFile.getAbsolutePath();
- resolver.delete(externalUri,
- "_data LIKE ?1 AND lower(_data)=lower(?2)",
- new String[] { path, path });
}
}
diff --git a/core/java/com/android/internal/os/BatteryStatsHelper.java b/core/java/com/android/internal/os/BatteryStatsHelper.java
index f3632f0..f085e29 100644
--- a/core/java/com/android/internal/os/BatteryStatsHelper.java
+++ b/core/java/com/android/internal/os/BatteryStatsHelper.java
@@ -840,7 +840,10 @@
if (sipper.shouldHide) {
if (sipper.drainType != BatterySipper.DrainType.OVERCOUNTED
&& sipper.drainType != BatterySipper.DrainType.SCREEN
- && sipper.drainType != BatterySipper.DrainType.UNACCOUNTED) {
+ && sipper.drainType != BatterySipper.DrainType.UNACCOUNTED
+ && sipper.drainType != BatterySipper.DrainType.BLUETOOTH
+ && sipper.drainType != BatterySipper.DrainType.WIFI
+ && sipper.drainType != BatterySipper.DrainType.IDLE) {
// Don't add it if it is overcounted, unaccounted or screen
proportionalSmearPowerMah += sipper.totalPowerMah;
}
@@ -861,19 +864,19 @@
* time.
*/
public void smearScreenBatterySipper(List<BatterySipper> sippers, BatterySipper screenSipper) {
- final long rawRealtimeMs = SystemClock.elapsedRealtime();
long totalActivityTimeMs = 0;
final SparseLongArray activityTimeArray = new SparseLongArray();
for (int i = 0, size = sippers.size(); i < size; i++) {
final BatteryStats.Uid uid = sippers.get(i).uidObj;
if (uid != null) {
- final long timeMs = getForegroundActivityTotalTimeMs(uid, rawRealtimeMs);
+ final long timeMs = getProcessForegroundTimeMs(uid,
+ BatteryStats.STATS_SINCE_CHARGED);
activityTimeArray.put(uid.getUid(), timeMs);
totalActivityTimeMs += timeMs;
}
}
- if (totalActivityTimeMs >= 10 * DateUtils.MINUTE_IN_MILLIS) {
+ if (screenSipper != null && totalActivityTimeMs >= 10 * DateUtils.MINUTE_IN_MILLIS) {
final double screenPowerMah = screenSipper.totalPowerMah;
for (int i = 0, size = sippers.size(); i < size; i++) {
final BatterySipper sipper = sippers.get(i);
@@ -936,17 +939,42 @@
return false;
}
+ public long convertUsToMs(long timeUs) {
+ return timeUs / 1000;
+ }
+
+ public long convertMsToUs(long timeMs) {
+ return timeMs * 1000;
+ }
+
@VisibleForTesting
- public long getForegroundActivityTotalTimeMs(BatteryStats.Uid uid, long rawRealtimeMs) {
+ public long getForegroundActivityTotalTimeUs(BatteryStats.Uid uid, long rawRealtimeUs) {
final BatteryStats.Timer timer = uid.getForegroundActivityTimer();
if (timer != null) {
- return timer.getTotalTimeLocked(rawRealtimeMs, BatteryStats.STATS_SINCE_CHARGED);
+ return timer.getTotalTimeLocked(rawRealtimeUs, BatteryStats.STATS_SINCE_CHARGED);
}
return 0;
}
@VisibleForTesting
+ public long getProcessForegroundTimeMs(BatteryStats.Uid uid, int which) {
+ final long rawRealTimeUs = convertMsToUs(SystemClock.elapsedRealtime());
+ final int foregroundTypes[] = {BatteryStats.Uid.PROCESS_STATE_TOP};
+
+ long timeUs = 0;
+ for (int type : foregroundTypes) {
+ final long localTime = uid.getProcessStateTime(type, rawRealTimeUs, which);
+ timeUs += localTime;
+ }
+
+ // Return the min value of STATE_TOP time and foreground activity time, since both of these
+ // time have some errors.
+ return convertUsToMs(
+ Math.min(timeUs, getForegroundActivityTotalTimeUs(uid, rawRealTimeUs)));
+ }
+
+ @VisibleForTesting
public void setPackageManager(PackageManager packageManager) {
mPackageManager = packageManager;
}
diff --git a/core/java/com/android/internal/os/WebViewZygoteInit.java b/core/java/com/android/internal/os/WebViewZygoteInit.java
index e28079f..58e4a3e 100644
--- a/core/java/com/android/internal/os/WebViewZygoteInit.java
+++ b/core/java/com/android/internal/os/WebViewZygoteInit.java
@@ -26,6 +26,7 @@
import android.webkit.WebViewFactory;
import android.webkit.WebViewFactoryProvider;
+import java.io.DataOutputStream;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
@@ -87,17 +88,28 @@
// Once we have the classloader, look up the WebViewFactoryProvider implementation and
// call preloadInZygote() on it to give it the opportunity to preload the native library
// and perform any other initialisation work that should be shared among the children.
+ boolean preloadSucceeded = false;
try {
Class<WebViewFactoryProvider> providerClass =
WebViewFactory.getWebViewProviderClass(loader);
Object result = providerClass.getMethod("preloadInZygote").invoke(null);
- if (!((Boolean)result).booleanValue()) {
+ preloadSucceeded = ((Boolean) result).booleanValue();
+ if (!preloadSucceeded) {
Log.e(TAG, "preloadInZygote returned false");
}
} catch (ClassNotFoundException | NoSuchMethodException | SecurityException |
IllegalAccessException | InvocationTargetException e) {
Log.e(TAG, "Exception while preloading package", e);
}
+
+ try {
+ DataOutputStream socketOut = getSocketOutputStream();
+ socketOut.writeInt(preloadSucceeded ? 1 : 0);
+ } catch (IOException ioe) {
+ Log.e(TAG, "Error writing to command socket", ioe);
+ return true;
+ }
+
Log.i(TAG, "Package preload done");
return false;
}
diff --git a/core/java/com/android/internal/os/ZygoteConnection.java b/core/java/com/android/internal/os/ZygoteConnection.java
index 2013ac0..f0c0b99 100644
--- a/core/java/com/android/internal/os/ZygoteConnection.java
+++ b/core/java/com/android/internal/os/ZygoteConnection.java
@@ -314,6 +314,10 @@
return ZygoteInit.isPreloadComplete();
}
+ protected DataOutputStream getSocketOutputStream() {
+ return mSocketOutStream;
+ }
+
protected boolean handlePreloadPackage(String packagePath, String libsPath, String cacheKey) {
throw new RuntimeException("Zyogte does not support package preloading");
}
diff --git a/core/res/res/layout/chooser_row.xml b/core/res/res/layout/chooser_row.xml
index 9baa32c..6c1271d 100644
--- a/core/res/res/layout/chooser_row.xml
+++ b/core/res/res/layout/chooser_row.xml
@@ -18,11 +18,9 @@
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
- android:layout_width="match_parent" android:layout_height="wrap_content"
- android:minHeight="80dp"
+ android:layout_width="match_parent"
+ android:layout_height="100dp"
android:gravity="start|top"
- android:paddingTop="8dp"
- android:paddingBottom="8dp"
android:paddingStart="@dimen/chooser_grid_padding"
android:paddingEnd="@dimen/chooser_grid_padding"
android:weightSum="4">
diff --git a/core/res/res/layout/resolve_grid_item.xml b/core/res/res/layout/resolve_grid_item.xml
index 305c8b0..71c153f 100644
--- a/core/res/res/layout/resolve_grid_item.xml
+++ b/core/res/res/layout/resolve_grid_item.xml
@@ -18,10 +18,9 @@
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
- android:layout_width="0dp"
+ android:layout_width="76dp"
android:layout_height="wrap_content"
- android:layout_weight="1"
- android:minWidth="80dp"
+ android:minHeight="100dp"
android:gravity="center"
android:paddingTop="8dp"
android:paddingBottom="8dp"
@@ -49,7 +48,7 @@
<TextView android:id="@android:id/text1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginTop="8dp"
+ android:layout_marginTop="4dp"
android:layout_marginLeft="4dp"
android:layout_marginRight="4dp"
android:textAppearance="?attr/textAppearanceSmall"
diff --git a/core/res/res/layout/shutdown_dialog.xml b/core/res/res/layout/shutdown_dialog.xml
new file mode 100644
index 0000000..398bfb1
--- /dev/null
+++ b/core/res/res/layout/shutdown_dialog.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2014 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.
+-->
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:gravity="center_horizontal" >
+
+ <Space
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="6" />
+
+ <TextView
+ android:id="@id/text1"
+ android:layout_width="wrap_content"
+ android:layout_height="32dp"
+ android:text="@string/shutdown_progress"
+ android:textDirection="locale"
+ android:textSize="24sp"
+ android:textAppearance="?attr/textAppearanceLarge"
+ android:gravity="center"
+ android:layout_marginBottom="24dp"
+ android:fontFamily="@string/config_headlineFontFamily"/>
+
+ <ProgressBar
+ android:id="@id/progress"
+ android:layout_width="30dp"
+ android:layout_height="30dp"
+ style="?attr/progressBarStyleLarge" />
+
+ <Space
+ android:layout_width="match_parent"
+ android:layout_height="0dp"
+ android:layout_weight="10" />
+
+</LinearLayout>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 69bb0b9..ab909cf 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -284,6 +284,13 @@
Settings.Global.NETWORK_AVOID_BAD_WIFI. This is the default value of that setting. -->
<integer translatable="false" name="config_networkAvoidBadWifi">1</integer>
+ <!-- If the hardware supports specially marking packets that caused a wakeup of the
+ main CPU, set this value to the mark used. -->
+ <integer name="config_networkWakeupPacketMark">0</integer>
+
+ <!-- Mask to use when checking skb mark defined in config_networkWakeupPacketMark above. -->
+ <integer name="config_networkWakeupPacketMask">0</integer>
+
<!-- Default value for ConnectivityManager.getMultipathPreference() on metered networks. Actual
device behaviour is controlled by Settings.Global.NETWORK_METERED_MULTIPATH_PREFERENCE.
This is the default value of that setting. -->
diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml
index fa33d56..9f9c883 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -533,6 +533,8 @@
<dimen name="content_rect_bottom_clip_allowance">20dp</dimen>
<dimen name="chooser_grid_padding">0dp</dimen>
+ <!-- Spacing around the background change frome service to non-service -->
+ <dimen name="chooser_service_spacing">8dp</dimen>
<item type="dimen" name="aerr_padding_list_top">15dp</item>
<item type="dimen" name="aerr_padding_list_bottom">8dp</item>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 99807f9..ad9b5af 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1838,6 +1838,8 @@
<java-symbol type="integer" name="config_networkNotifySwitchType" />
<java-symbol type="array" name="config_networkNotifySwitches" />
<java-symbol type="integer" name="config_networkAvoidBadWifi" />
+ <java-symbol type="integer" name="config_networkWakeupPacketMark" />
+ <java-symbol type="integer" name="config_networkWakeupPacketMask" />
<java-symbol type="integer" name="config_networkMeteredMultipathPreference" />
<java-symbol type="integer" name="config_notificationsBatteryFullARGB" />
<java-symbol type="integer" name="config_notificationsBatteryLedOff" />
@@ -3058,4 +3060,6 @@
<java-symbol type="array" name="config_batteryPackageTypeSystem" />
<java-symbol type="array" name="config_batteryPackageTypeService" />
<java-symbol type="bool" name="config_showAreaUpdateInfoSettings" />
+ <java-symbol type="layout" name="shutdown_dialog" />
+ <java-symbol type="dimen" name="chooser_service_spacing" />
</resources>
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsHelperTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsHelperTest.java
index f01c33f..e81f678 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsHelperTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsHelperTest.java
@@ -18,9 +18,12 @@
package com.android.internal.os;
+import static android.os.BatteryStats.Uid.PROCESS_STATE_TOP;
+
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyLong;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
@@ -54,11 +57,16 @@
@SmallTest
public class BatteryStatsHelperTest extends TestCase {
private static final long TIME_FOREGROUND_ACTIVITY_ZERO = 0;
- private static final long TIME_FOREGROUND_ACTIVITY = 100 * DateUtils.MINUTE_IN_MILLIS;
+ private static final long TIME_FOREGROUND_ACTIVITY = 100 * DateUtils.MINUTE_IN_MILLIS * 1000;
+ private static final long TIME_STATE_FOREGROUND_MS = 10 * DateUtils.MINUTE_IN_MILLIS;
+ private static final long TIME_STATE_FOREGROUND_US = TIME_STATE_FOREGROUND_MS * 1000;
private static final int UID = 123456;
private static final double BATTERY_SCREEN_USAGE = 300;
private static final double BATTERY_SYSTEM_USAGE = 600;
+ private static final double BATTERY_WIFI_USAGE = 200;
+ private static final double BATTERY_IDLE_USAGE = 600;
+ private static final double BATTERY_BLUETOOTH_USAGE = 300;
private static final double BATTERY_OVERACCOUNTED_USAGE = 500;
private static final double BATTERY_UNACCOUNTED_USAGE = 700;
private static final double BATTERY_APP_USAGE = 100;
@@ -68,6 +76,12 @@
@Mock
private BatteryStats.Uid mUid;
@Mock
+ private BatterySipper mWifiBatterySipper;
+ @Mock
+ private BatterySipper mBluetoothBatterySipper;
+ @Mock
+ private BatterySipper mIdleBatterySipper;
+ @Mock
private BatterySipper mNormalBatterySipper;
@Mock
private BatterySipper mScreenBatterySipper;
@@ -109,6 +123,15 @@
mUnaccountedBatterySipper.drainType = BatterySipper.DrainType.UNACCOUNTED;
mUnaccountedBatterySipper.totalPowerMah = BATTERY_UNACCOUNTED_USAGE;
+ mWifiBatterySipper.drainType = BatterySipper.DrainType.WIFI;
+ mWifiBatterySipper.totalPowerMah = BATTERY_WIFI_USAGE;
+
+ mBluetoothBatterySipper.drainType = BatterySipper.DrainType.BLUETOOTH;
+ mBluetoothBatterySipper.totalPowerMah = BATTERY_BLUETOOTH_USAGE;
+
+ mIdleBatterySipper.drainType = BatterySipper.DrainType.IDLE;
+ mIdleBatterySipper.totalPowerMah = BATTERY_IDLE_USAGE;
+
mContext = InstrumentationRegistry.getContext();
mBatteryStatsHelper = spy(new BatteryStatsHelper(mContext));
mBatteryStatsHelper.setPackageManager(mPackageManager);
@@ -165,6 +188,9 @@
sippers.add(mSystemBatterySipper);
sippers.add(mOvercountedBatterySipper);
sippers.add(mUnaccountedBatterySipper);
+ sippers.add(mWifiBatterySipper);
+ sippers.add(mBluetoothBatterySipper);
+ sippers.add(mIdleBatterySipper);
doReturn(true).when(mBatteryStatsHelper).isTypeSystem(mSystemBatterySipper);
doNothing().when(mBatteryStatsHelper).smearScreenBatterySipper(any(), any());
@@ -219,6 +245,19 @@
assertThat(mBatteryStatsHelper.isTypeService(mNormalBatterySipper)).isTrue();
}
+ @Test
+ public void testGetProcessForegroundTimeMs_largerActivityTime_returnMinTime() {
+ doReturn(TIME_STATE_FOREGROUND_US + 500).when(mBatteryStatsHelper)
+ .getForegroundActivityTotalTimeUs(eq(mUid), anyLong());
+ doReturn(TIME_STATE_FOREGROUND_US).when(mUid).getProcessStateTime(eq(PROCESS_STATE_TOP),
+ anyLong(), anyInt());
+
+ final long time = mBatteryStatsHelper.getProcessForegroundTimeMs(mUid,
+ BatteryStats.STATS_SINCE_CHARGED);
+
+ assertThat(time).isEqualTo(TIME_STATE_FOREGROUND_MS);
+ }
+
private BatterySipper createTestSmearBatterySipper(long activityTime, double totalPowerMah,
int uidCode, boolean isUidNull) {
final BatterySipper sipper = mock(BatterySipper.class);
@@ -227,8 +266,8 @@
doReturn(uidCode).when(sipper).getUid();
if (!isUidNull) {
final BatteryStats.Uid uid = mock(BatteryStats.Uid.class, RETURNS_DEEP_STUBS);
- doReturn(activityTime).when(mBatteryStatsHelper).getForegroundActivityTotalTimeMs(
- eq(uid), anyLong());
+ doReturn(activityTime).when(mBatteryStatsHelper).getProcessForegroundTimeMs(eq(uid),
+ anyInt());
doReturn(uidCode).when(uid).getUid();
sipper.uidObj = uid;
}
@@ -236,5 +275,4 @@
return sipper;
}
-
}
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index a430288..064cc84 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -465,6 +465,8 @@
<string name="wifi_allow_scan_with_traffic">Always allow Wi\u2011Fi Roam Scans</string>
<!-- Setting Checkbox title whether to always keep mobile data active. [CHAR LIMIT=80] -->
<string name="mobile_data_always_on">Mobile data always active</string>
+ <!-- Setting Checkbox title whether to enable hardware acceleration for tethering. [CHAR LIMIT=80] -->
+ <string name="tethering_hardware_offload">Tethering hardware acceleration</string>
<!-- Setting Checkbox title for disabling Bluetooth absolute volume -->
<string name="bluetooth_disable_absolute_volume">Disable absolute volume</string>
<!-- Setting Checkbox title for enabling Bluetooth inband ringing -->
@@ -534,6 +536,7 @@
<!-- Setting Checkbox title whether to enable view attribute inspection -->
<string name="debug_view_attributes">Enable view attribute inspection</string>
<string name="mobile_data_always_on_summary">Always keep mobile data active, even when Wi\u2011Fi is active (for fast network switching).</string>
+ <string name="tethering_hardware_offload_summary">Use tethering hardware acceleration if available</string>
<!-- Title of warning dialog about the implications of enabling USB debugging -->
<string name="adb_warning_title">Allow USB debugging?</string>
<!-- Warning text to user about the implications of enabling USB debugging -->
diff --git a/packages/SettingsLib/src/com/android/settingslib/Utils.java b/packages/SettingsLib/src/com/android/settingslib/Utils.java
index 5767823..dee5a93 100644
--- a/packages/SettingsLib/src/com/android/settingslib/Utils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/Utils.java
@@ -130,7 +130,7 @@
}
/** Formats a double from 0.0..1.0 as a percentage. */
- private static String formatPercentage(double percentage) {
+ public static String formatPercentage(double percentage) {
return NumberFormat.getPercentInstance().format(percentage);
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/InterestingConfigChanges.java b/packages/SettingsLib/src/com/android/settingslib/applications/InterestingConfigChanges.java
index e4e0f7f..c4cbc2b 100644
--- a/packages/SettingsLib/src/com/android/settingslib/applications/InterestingConfigChanges.java
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/InterestingConfigChanges.java
@@ -36,7 +36,8 @@
}
public boolean applyNewConfig(Resources res) {
- int configChanges = mLastConfiguration.updateFrom(res.getConfiguration());
+ int configChanges = mLastConfiguration.updateFrom(
+ Configuration.generateDelta(mLastConfiguration, res.getConfiguration()));
boolean densityChanged = mLastDensity != res.getDisplayMetrics().densityDpi;
if (densityChanged || (configChanges & (mFlags)) != 0) {
mLastDensity = res.getDisplayMetrics().densityDpi;
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
index 80b943c..d07da93 100755
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
@@ -102,6 +102,7 @@
// Fine-grained state broadcasts
addHandler(BluetoothDevice.ACTION_CLASS_CHANGED, new ClassChangedHandler());
addHandler(BluetoothDevice.ACTION_UUID, new UuidChangedHandler());
+ addHandler(BluetoothDevice.ACTION_BATTERY_LEVEL_CHANGED, new BatteryLevelChangedHandler());
// Dock event broadcasts
addHandler(Intent.ACTION_DOCK_EVENT, new DockEventHandler());
@@ -376,6 +377,17 @@
}
}
}
+
+ private class BatteryLevelChangedHandler implements Handler {
+ public void onReceive(Context context, Intent intent,
+ BluetoothDevice device) {
+ CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device);
+ if (cachedDevice != null) {
+ cachedDevice.refresh();
+ }
+ }
+ }
+
boolean readPairedDevices() {
Set<BluetoothDevice> bondedDevices = mLocalAdapter.getBondedDevices();
if (bondedDevices == null) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index e279a09..4bb4b40 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -368,6 +368,15 @@
return mDevice;
}
+ /**
+ * Convenience method that can be mocked - it lets tests avoid having to call getDevice() which
+ * causes problems in tests since BluetoothDevice is final and cannot be mocked.
+ * @return the address of this device
+ */
+ public String getAddress() {
+ return mDevice.getAddress();
+ }
+
public String getName() {
return mName;
}
@@ -410,6 +419,14 @@
}
}
+ /**
+ * Get battery level from remote device
+ * @return battery level in percentage [0-100], or {@link BluetoothDevice#BATTERY_LEVEL_UNKNOWN}
+ */
+ public int getBatteryLevel() {
+ return mDevice.getBatteryLevel();
+ }
+
void refresh() {
dispatchAttributesChanged();
}
@@ -828,7 +845,7 @@
/**
* @return resource for string that discribes the connection state of this device.
*/
- public int getConnectionSummary() {
+ public String getConnectionSummary() {
boolean profileConnected = false; // at least one profile is connected
boolean a2dpNotConnected = false; // A2DP is preferred but not connected
boolean hfpNotConnected = false; // HFP is preferred but not connected
@@ -839,7 +856,7 @@
switch (connectionStatus) {
case BluetoothProfile.STATE_CONNECTING:
case BluetoothProfile.STATE_DISCONNECTING:
- return Utils.getConnectionStateSummary(connectionStatus);
+ return mContext.getString(Utils.getConnectionStateSummary(connectionStatus));
case BluetoothProfile.STATE_CONNECTED:
profileConnected = true;
@@ -859,18 +876,54 @@
}
}
+ String batteryLevelPercentageString = null;
+ // Android framework should only set mBatteryLevel to valid range [0-100] or
+ // BluetoothDevice.BATTERY_LEVEL_UNKNOWN, any other value should be a framework bug.
+ // Thus assume here that if value is not BluetoothDevice.BATTERY_LEVEL_UNKNOWN, it must
+ // be valid
+ final int batteryLevel = getBatteryLevel();
+ if (batteryLevel != BluetoothDevice.BATTERY_LEVEL_UNKNOWN) {
+ // TODO: name com.android.settingslib.bluetooth.Utils something different
+ batteryLevelPercentageString =
+ com.android.settingslib.Utils.formatPercentage(batteryLevel);
+ }
+
if (profileConnected) {
if (a2dpNotConnected && hfpNotConnected) {
- return R.string.bluetooth_connected_no_headset_no_a2dp;
+ if (batteryLevelPercentageString != null) {
+ return mContext.getString(
+ R.string.bluetooth_connected_no_headset_no_a2dp_battery_level,
+ batteryLevelPercentageString);
+ } else {
+ return mContext.getString(R.string.bluetooth_connected_no_headset_no_a2dp);
+ }
+
} else if (a2dpNotConnected) {
- return R.string.bluetooth_connected_no_a2dp;
+ if (batteryLevelPercentageString != null) {
+ return mContext.getString(R.string.bluetooth_connected_no_a2dp_battery_level,
+ batteryLevelPercentageString);
+ } else {
+ return mContext.getString(R.string.bluetooth_connected_no_a2dp);
+ }
+
} else if (hfpNotConnected) {
- return R.string.bluetooth_connected_no_headset;
+ if (batteryLevelPercentageString != null) {
+ return mContext.getString(R.string.bluetooth_connected_no_headset_battery_level,
+ batteryLevelPercentageString);
+ } else {
+ return mContext.getString(R.string.bluetooth_connected_no_headset);
+ }
} else {
- return R.string.bluetooth_connected;
+ if (batteryLevelPercentageString != null) {
+ return mContext.getString(R.string.bluetooth_connected_battery_level,
+ batteryLevelPercentageString);
+ } else {
+ return mContext.getString(R.string.bluetooth_connected);
+ }
}
}
- return getBondState() == BluetoothDevice.BOND_BONDING ? R.string.bluetooth_pairing : 0;
+ return getBondState() == BluetoothDevice.BOND_BONDING ?
+ mContext.getString(R.string.bluetooth_pairing) : null;
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java
index 5529866..d45fe1a 100755
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java
@@ -34,7 +34,7 @@
/**
* HeadsetProfile handles Bluetooth HFP and Headset profiles.
*/
-public final class HeadsetProfile implements LocalBluetoothProfile {
+public class HeadsetProfile implements LocalBluetoothProfile {
private static final String TAG = "HeadsetProfile";
private static boolean V = true;
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java
index a9e8db5..d1621da 100755
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HidProfile.java
@@ -31,7 +31,7 @@
/**
* HidProfile handles Bluetooth HID profile.
*/
-public final class HidProfile implements LocalBluetoothProfile {
+public class HidProfile implements LocalBluetoothProfile {
private static final String TAG = "HidProfile";
private static boolean V = true;
diff --git a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
index 4a54451..ec41415 100644
--- a/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
+++ b/packages/SettingsLib/src/com/android/settingslib/wifi/AccessPoint.java
@@ -1053,7 +1053,8 @@
}
/** Attempt to update the AccessPoint and return true if an update occurred. */
- public boolean update(WifiConfiguration config, WifiInfo info, NetworkInfo networkInfo) {
+ public boolean update(
+ @Nullable WifiConfiguration config, WifiInfo info, NetworkInfo networkInfo) {
boolean updated = false;
final int oldLevel = getLevel();
if (info != null && isInfoForThisAccessPoint(config, info)) {
@@ -1088,9 +1089,9 @@
return updated;
}
- void update(WifiConfiguration config) {
+ void update(@Nullable WifiConfiguration config) {
mConfig = config;
- networkId = config.networkId;
+ networkId = config != null ? config.networkId : WifiConfiguration.INVALID_NETWORK_ID;
if (mAccessPointListener != null) {
mAccessPointListener.onAccessPointChanged(this);
}
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 5c64cff..c08dd6e 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
@@ -656,4 +656,28 @@
assertThat(ap.update(newConfig, wifiInfo, networkInfo)).isFalse();
verify(mockListener).onAccessPointChanged(ap);
}
+
+ @Test
+ public void testUpdateWithNullWifiConfiguration_doesNotThrowNPE() {
+ int networkId = 123;
+ int rssi = -55;
+ WifiConfiguration config = new WifiConfiguration();
+ config.networkId = networkId;
+ WifiInfo wifiInfo = new WifiInfo();
+ wifiInfo.setNetworkId(networkId);
+ wifiInfo.setRssi(rssi);
+
+ NetworkInfo networkInfo =
+ new NetworkInfo(ConnectivityManager.TYPE_WIFI, 0 /* subtype */, "WIFI", "");
+ networkInfo.setDetailedState(NetworkInfo.DetailedState.CONNECTING, "", "");
+
+ AccessPoint ap = new TestAccessPointBuilder(mContext)
+ .setNetworkInfo(networkInfo)
+ .setNetworkId(networkId)
+ .setRssi(rssi)
+ .setWifiInfo(wifiInfo)
+ .build();
+
+ ap.update(null, wifiInfo, networkInfo);
+ }
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
new file mode 100644
index 0000000..e2ebbeb
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
@@ -0,0 +1,155 @@
+/*
+ * 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.settingslib.bluetooth;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothProfile;
+import android.content.Context;
+
+import com.android.settingslib.R;
+import com.android.settingslib.TestConfig;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
+
+@RunWith(RobolectricTestRunner.class)
+@Config(manifest = TestConfig.MANIFEST_PATH, sdk = TestConfig.SDK_VERSION, resourceDir =
+ "../../res")
+public class CachedBluetoothDeviceTest {
+ @Mock
+ private LocalBluetoothAdapter mAdapter;
+ @Mock
+ private LocalBluetoothProfileManager mProfileManager;
+ @Mock
+ private HeadsetProfile mHfpProfile;
+ @Mock
+ private A2dpProfile mA2dpProfile;
+ @Mock
+ private HidProfile mHidProfile;
+ @Mock
+ private BluetoothDevice mDevice;
+ private CachedBluetoothDevice mCachedDevice;
+ private Context mContext;
+ private int mBatteryLevel = BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mContext = RuntimeEnvironment.application;
+ when(mAdapter.getBluetoothState()).thenReturn(BluetoothAdapter.STATE_ON);
+ when(mHfpProfile.isProfileReady()).thenReturn(true);
+ when(mA2dpProfile.isProfileReady()).thenReturn(true);
+ when(mHidProfile.isProfileReady()).thenReturn(true);
+ mCachedDevice = spy(
+ new CachedBluetoothDevice(mContext, mAdapter, mProfileManager, mDevice));
+ doAnswer((invocation) -> mBatteryLevel).when(mCachedDevice).getBatteryLevel();
+ }
+
+ /**
+ * Test to verify the current test context object works so that we are not checking null
+ * against null
+ */
+ @Test
+ public void testContextMock() {
+ assertThat(mContext.getString(R.string.bluetooth_connected)).isEqualTo("Connected");
+ }
+
+ @Test
+ public void testGetConnectionSummary_testSingleProfileConnectDisconnect() {
+ // Test without battery level
+ // Set HID profile to be connected and test connection state summary
+ mCachedDevice.onProfileStateChanged(mHidProfile, BluetoothProfile.STATE_CONNECTED);
+ assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(mContext.getString(
+ R.string.bluetooth_connected));
+
+ // Set HID profile to be disconnected and test connection state summary
+ mCachedDevice.onProfileStateChanged(mHidProfile, BluetoothProfile.STATE_DISCONNECTED);
+ assertThat(mCachedDevice.getConnectionSummary()).isNull();
+
+ // Test with battery level
+ mBatteryLevel = 10;
+ // Set HID profile to be connected and test connection state summary
+ mCachedDevice.onProfileStateChanged(mHidProfile, BluetoothProfile.STATE_CONNECTED);
+ assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(mContext.getString(
+ R.string.bluetooth_connected_battery_level,
+ com.android.settingslib.Utils.formatPercentage(mBatteryLevel)));
+
+ // Set HID profile to be disconnected and test connection state summary
+ mCachedDevice.onProfileStateChanged(mHidProfile, BluetoothProfile.STATE_DISCONNECTED);
+ assertThat(mCachedDevice.getConnectionSummary()).isNull();
+
+ // Test with BluetoothDevice.BATTERY_LEVEL_UNKNOWN battery level
+ mBatteryLevel = BluetoothDevice.BATTERY_LEVEL_UNKNOWN;
+
+ // Set HID profile to be connected and test connection state summary
+ mCachedDevice.onProfileStateChanged(mHidProfile, BluetoothProfile.STATE_CONNECTED);
+ assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(mContext.getString(
+ R.string.bluetooth_connected));
+
+ // Set HID profile to be disconnected and test connection state summary
+ mCachedDevice.onProfileStateChanged(mHidProfile, BluetoothProfile.STATE_DISCONNECTED);
+ assertThat(mCachedDevice.getConnectionSummary()).isNull();
+ }
+
+ @Test
+ public void testGetConnectionSummary_testMultipleProfileConnectDisconnect() {
+ mBatteryLevel = 10;
+
+ // Set HFP, A2DP and HID profile to be connected and test connection state summary
+ mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
+ mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_CONNECTED);
+ mCachedDevice.onProfileStateChanged(mHidProfile, BluetoothProfile.STATE_CONNECTED);
+ assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(mContext.getString(
+ R.string.bluetooth_connected_battery_level,
+ com.android.settingslib.Utils.formatPercentage(mBatteryLevel)));
+
+ // Disconnect HFP only and test connection state summary
+ mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED);
+ assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(mContext.getString(
+ R.string.bluetooth_connected_no_headset_battery_level,
+ com.android.settingslib.Utils.formatPercentage(mBatteryLevel)));
+
+ // Disconnect A2DP only and test connection state summary
+ mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_CONNECTED);
+ mCachedDevice.onProfileStateChanged(mA2dpProfile, BluetoothProfile.STATE_DISCONNECTED);
+ assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(mContext.getString(
+ R.string.bluetooth_connected_no_a2dp_battery_level,
+ com.android.settingslib.Utils.formatPercentage(mBatteryLevel)));
+
+ // Disconnect both HFP and A2DP and test connection state summary
+ mCachedDevice.onProfileStateChanged(mHfpProfile, BluetoothProfile.STATE_DISCONNECTED);
+ assertThat(mCachedDevice.getConnectionSummary()).isEqualTo(mContext.getString(
+ R.string.bluetooth_connected_no_headset_no_a2dp_battery_level,
+ com.android.settingslib.Utils.formatPercentage(mBatteryLevel)));
+
+ // Disconnect all profiles and test connection state summary
+ mCachedDevice.onProfileStateChanged(mHidProfile, BluetoothProfile.STATE_DISCONNECTED);
+ assertThat(mCachedDevice.getConnectionSummary()).isNull();
+ }
+}
diff --git a/packages/SystemUI/res/drawable/brightness_mirror_background.xml b/packages/SystemUI/res/drawable/brightness_mirror_background.xml
index 0c69d89..b3a0484 100644
--- a/packages/SystemUI/res/drawable/brightness_mirror_background.xml
+++ b/packages/SystemUI/res/drawable/brightness_mirror_background.xml
@@ -15,5 +15,5 @@
~ limitations under the License
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android">
- <solid android:color="?android:attr/colorPrimary" />
+ <solid android:color="@color/qs_background_dark" />
</shape>
diff --git a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
index 2ff626a..f8b7b8a 100644
--- a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
+++ b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
@@ -33,13 +33,13 @@
<LinearLayout
android:layout_width="match_parent"
- android:layout_height="32dp"
+ android:layout_height="40dp"
android:layout_alignParentEnd="true"
android:clipChildren="false"
android:clipToPadding="false"
android:gravity="center"
- android:paddingStart="16dp"
- android:paddingEnd="16dp"
+ android:paddingStart="8dp"
+ android:paddingEnd="8dp"
android:orientation="horizontal">
diff --git a/packages/SystemUI/res/layout/recents_empty.xml b/packages/SystemUI/res/layout/recents_empty.xml
index 53d9cc5..8048c68 100644
--- a/packages/SystemUI/res/layout/recents_empty.xml
+++ b/packages/SystemUI/res/layout/recents_empty.xml
@@ -23,7 +23,8 @@
android:drawableTop="@drawable/recents_empty"
android:drawablePadding="25dp"
android:textSize="16sp"
- android:textColor="#ffffffff"
+ android:drawableTint="?attr/bgProtectTextColor"
+ android:textColor="?attr/bgProtectTextColor"
android:text="@string/recents_empty_message"
android:fontFamily="sans-serif"
android:visibility="gone" />
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/recents_stack_action_button.xml b/packages/SystemUI/res/layout/recents_stack_action_button.xml
index 34b4e17..10b4316 100644
--- a/packages/SystemUI/res/layout/recents_stack_action_button.xml
+++ b/packages/SystemUI/res/layout/recents_stack_action_button.xml
@@ -24,7 +24,7 @@
android:paddingBottom="12dp"
android:text="@string/recents_stack_action_button_label"
android:textSize="14sp"
- android:textColor="#FFFFFF"
+ android:textColor="?attr/bgProtectTextColor"
android:textAllCaps="true"
android:shadowColor="#99000000"
android:shadowDx="0"
diff --git a/packages/SystemUI/res/layout/rounded_corners.xml b/packages/SystemUI/res/layout/rounded_corners.xml
index 8d391e0..d1ef5d6 100644
--- a/packages/SystemUI/res/layout/rounded_corners.xml
+++ b/packages/SystemUI/res/layout/rounded_corners.xml
@@ -22,6 +22,7 @@
android:id="@+id/left"
android:layout_width="12dp"
android:layout_height="12dp"
+ android:layout_gravity="left"
android:tint="#ff000000"
android:src="@drawable/rounded" />
<ImageView
@@ -29,6 +30,6 @@
android:layout_width="12dp"
android:layout_height="12dp"
android:tint="#ff000000"
- android:layout_gravity="end"
+ android:layout_gravity="right"
android:src="@drawable/rounded" />
</FrameLayout>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 5274b64..93a0742 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -16,7 +16,7 @@
<resources xmlns:android="http://schemas.android.com/apk/res/android">
- <style name="RecentsTheme" parent="@android:style/Theme.Material">
+ <style name="RecentsTheme" parent="RecentsBase">
<!-- NoTitle -->
<item name="android:windowNoTitle">true</item>
<!-- Misc -->
@@ -27,6 +27,12 @@
<item name="android:ambientShadowAlpha">0.35</item>
</style>
+ <!-- OverlayManager might replace this style entirely, use RecentsTheme to set a property
+ that should exist in both light and dark versions of Recents -->
+ <style name="RecentsBase" parent="@android:style/Theme.Material">
+ <item name="android:textColorPrimaryInverse">@*android:color/primary_text_material_dark</item>
+ <item name="android:textColorSecondaryInverse">@*android:color/secondary_text_material_dark</item>
+ </style>
<!-- Recents theme -->
<style name="RecentsTheme.Wallpaper">
@@ -35,6 +41,8 @@
<item name="android:windowShowWallpaper">true</item>
<item name="android:windowDisablePreview">true</item>
<item name="clearAllStyle">@style/ClearAllButtonDefaultMargins</item>
+ <item name="bgProtectTextColor">?android:attr/textColorPrimaryInverse</item>
+ <item name="bgProtectSecondaryTextColor">?android:attr/textColorSecondaryInverse</item>
</style>
<style name="ClearAllButtonDefaultMargins">
@@ -47,6 +55,8 @@
<!-- Performance optimized Recents theme (no wallpaper) -->
<style name="RecentsTheme.NoWallpaper">
<item name="android:windowBackground">@android:color/black</item>
+ <item name="bgProtectTextColor">@android:color/white</item>
+ <item name="bgProtectSecondaryTextColor">@android:color/white</item>
</style>
<!-- Theme used for the activity that shows when the system forced an app to be resizable -->
@@ -290,7 +300,10 @@
<style name="Animation.StatusBar">
</style>
- <style name="systemui_theme" parent="@*android:style/Theme.DeviceDefault.QuickSettings">
+ <!-- Overlay manager may replace this theme -->
+ <style name="systemui_base" parent="@*android:style/Theme.DeviceDefault.QuickSettings" />
+
+ <style name="systemui_theme" parent="systemui_base">
<item name="lightIconTheme">@style/DualToneLightTheme</item>
<item name="darkIconTheme">@style/DualToneDarkTheme</item>
<item name="bgProtectTextColor">?android:attr/textColorPrimaryInverse</item>
@@ -303,7 +316,10 @@
<item name="*android:successColor">?android:attr/textColorPrimaryInverse</item>
</style>
- <style name="qs_theme" parent="systemui_theme">
+ <!-- Overlay manager may replace this theme -->
+ <style name="qs_base" parent="@*android:style/Theme.DeviceDefault.QuickSettings" />
+
+ <style name="qs_theme" parent="qs_base">
<item name="lightIconTheme">@style/QSIconTheme</item>
<item name="darkIconTheme">@style/QSIconTheme</item>
</style>
@@ -459,7 +475,7 @@
<item name="android:paddingEnd">8dp</item>
</style>
- <style name="edit_theme" parent="@*android:style/Theme.DeviceDefault.QuickSettings">
+ <style name="edit_theme" parent="qs_base">
<item name="android:colorBackground">?android:attr/colorSecondary</item>
</style>
diff --git a/packages/SystemUI/src/com/android/systemui/ExpandHelper.java b/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
index 7fed3e8..377fab5 100644
--- a/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/ExpandHelper.java
@@ -545,6 +545,16 @@
*/
@VisibleForTesting
void finishExpanding(boolean forceAbort, float velocity) {
+ finishExpanding(forceAbort, velocity, true /* allowAnimation */);
+ }
+
+ /**
+ * Finish the current expand motion
+ * @param forceAbort whether the expansion should be forcefully aborted and returned to the old
+ * state
+ * @param velocity the velocity this was expanded/ collapsed with
+ */
+ private void finishExpanding(boolean forceAbort, float velocity, boolean allowAnimation) {
if (!mExpanding) return;
if (DEBUG) Log.d(TAG, "scale in finishing on view: " + mResizedView);
@@ -568,7 +578,7 @@
mCallback.expansionStateChanged(false);
int naturalHeight = mScaler.getNaturalHeight();
float targetHeight = nowExpanded ? naturalHeight : mSmallSize;
- if (targetHeight != currentHeight && mEnabled) {
+ if (targetHeight != currentHeight && mEnabled && allowAnimation) {
mScaleAnimation.setFloatValues(targetHeight);
mScaleAnimation.setupStartValues();
final View scaledView = mResizedView;
@@ -622,10 +632,22 @@
}
/**
+ * Use this to abort any pending expansions in progress and force that there will be no
+ * animations.
+ */
+ public void cancelImmediately() {
+ cancel(false /* allowAnimation */);
+ }
+
+ /**
* Use this to abort any pending expansions in progress.
*/
public void cancel() {
- finishExpanding(true /* forceAbort */, 0f /* velocity */);
+ cancel(true /* allowAnimation */);
+ }
+
+ private void cancel(boolean allowAnimation) {
+ finishExpanding(true /* forceAbort */, 0f /* velocity */, allowAnimation);
clearView();
// reset the gesture detector
diff --git a/packages/SystemUI/src/com/android/systemui/HardwareUiLayout.java b/packages/SystemUI/src/com/android/systemui/HardwareUiLayout.java
index bb0f2f9..22bb2a3 100644
--- a/packages/SystemUI/src/com/android/systemui/HardwareUiLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/HardwareUiLayout.java
@@ -19,6 +19,7 @@
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.content.Context;
+import android.content.res.Configuration;
import android.provider.Settings;
import android.util.AttributeSet;
import android.view.Gravity;
@@ -28,9 +29,15 @@
import android.view.ViewTreeObserver;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
-
import com.android.systemui.tuner.TunerService;
import com.android.systemui.tuner.TunerService.Tunable;
+import com.android.systemui.util.leak.RotationUtils;
+
+import java.util.ArrayList;
+
+import static com.android.systemui.util.leak.RotationUtils.ROTATION_LANDSCAPE;
+import static com.android.systemui.util.leak.RotationUtils.ROTATION_NONE;
+import static com.android.systemui.util.leak.RotationUtils.ROTATION_SEASCAPE;
public class HardwareUiLayout extends FrameLayout implements Tunable {
@@ -49,7 +56,7 @@
private int mEndPoint;
private boolean mEdgeBleed;
private boolean mRoundedDivider;
- private boolean mLandscape;
+ private int mRotation = ROTATION_NONE;
private boolean mRotatedBackground;
public HardwareUiLayout(Context context, AttributeSet attrs) {
@@ -93,8 +100,10 @@
private void updateEdgeMargin(int edge) {
if (mChild != null) {
MarginLayoutParams params = (MarginLayoutParams) mChild.getLayoutParams();
- if (mLandscape) {
+ if (mRotation == ROTATION_LANDSCAPE) {
params.topMargin = edge;
+ } else if (mRotation == ROTATION_SEASCAPE) {
+ params.bottomMargin = edge;
} else {
params.rightMargin = edge;
}
@@ -118,6 +127,7 @@
mChild.addOnLayoutChangeListener(
(v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) ->
updatePosition());
+ updateRotation();
} else {
return;
}
@@ -127,30 +137,69 @@
animateChild(mOldHeight, newHeight);
}
post(() -> updatePosition());
- boolean landscape = getMeasuredWidth() > getMeasuredHeight();
- if (landscape != mLandscape) {
- mLandscape = landscape;
- if (mLandscape) {
- toLandscape();
- if (mChild instanceof LinearLayout) {
- mRotatedBackground = true;
- mBackground.setRotatedBackground(true);
- ((LinearLayout) mChild).setOrientation(LinearLayout.HORIZONTAL);
- swapDimens(mChild);
+ }
+
+ @Override
+ protected void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ updateRotation();
+ }
+
+ private void updateRotation() {
+ int rotation = RotationUtils.getRotation(getContext());
+ if (rotation != mRotation) {
+ rotate(mRotation, rotation);
+ mRotation = rotation;
+ }
+ }
+
+ private void rotate(int from, int to) {
+ if (from != ROTATION_NONE && to != ROTATION_NONE) {
+ // Rather than handling this confusing case, just do 2 rotations.
+ rotate(from, ROTATION_NONE);
+ rotate(ROTATION_NONE, to);
+ return;
+ }
+ if (from == ROTATION_LANDSCAPE || to == ROTATION_SEASCAPE) {
+ rotateRight();
+ } else {
+ rotateLeft();
+ }
+ if (to != ROTATION_NONE) {
+ if (mChild instanceof LinearLayout) {
+ mRotatedBackground = true;
+ mBackground.setRotatedBackground(true);
+ LinearLayout linearLayout = (LinearLayout) mChild;
+ if (to == ROTATION_SEASCAPE) {
+ swapOrder(linearLayout);
}
- } else {
- fromLandscape();
- if (mChild instanceof LinearLayout) {
- mRotatedBackground = false;
- mBackground.setRotatedBackground(false);
- ((LinearLayout) mChild).setOrientation(LinearLayout.VERTICAL);
- swapDimens(mChild);
+ linearLayout.setOrientation(LinearLayout.HORIZONTAL);
+ swapDimens(this.mChild);
+ }
+ } else {
+ if (mChild instanceof LinearLayout) {
+ mRotatedBackground = false;
+ mBackground.setRotatedBackground(false);
+ LinearLayout linearLayout = (LinearLayout) mChild;
+ if (from == ROTATION_SEASCAPE) {
+ swapOrder(linearLayout);
}
+ linearLayout.setOrientation(LinearLayout.VERTICAL);
+ swapDimens(mChild);
}
}
}
- private void fromLandscape() {
+ private void swapOrder(LinearLayout linearLayout) {
+ ArrayList<View> children = new ArrayList<>();
+ for (int i = 0; i < linearLayout.getChildCount(); i++) {
+ children.add(0, linearLayout.getChildAt(i));
+ linearLayout.removeViewAt(i);
+ }
+ children.forEach(v -> linearLayout.addView(v));
+ }
+
+ private void rotateRight() {
rotateRight(this);
rotateRight(mChild);
swapDimens(this);
@@ -202,7 +251,7 @@
return retGravity;
}
- private void toLandscape() {
+ private void rotateLeft() {
rotateLeft(this);
rotateLeft(mChild);
swapDimens(this);
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java b/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
index 5aaa6c7..180b3ca 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeHost.java
@@ -31,6 +31,7 @@
void dozeTimeTick();
boolean isPowerSaveActive();
boolean isPulsingBlocked();
+ boolean isProvisioned();
void startPendingIntentDismissingKeyguard(PendingIntent intent);
void abortPulsing();
@@ -40,6 +41,7 @@
void onDoubleTap(float x, float y);
+
interface Callback {
default void onNotificationHeadsUp() {}
default void onPowerSaveChanged(boolean active) {}
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index 9981972..179f5b8 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -172,14 +172,6 @@
}
}
- private void onCarMode() {
- mMachine.requestState(DozeMachine.State.FINISH);
- }
-
- private void onPowerSave() {
- mMachine.requestState(DozeMachine.State.FINISH);
- }
-
@Override
public void transitionTo(DozeMachine.State oldState, DozeMachine.State newState) {
switch (newState) {
@@ -215,11 +207,10 @@
}
private void checkTriggersAtInit() {
- if (mUiModeManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_CAR) {
- onCarMode();
- }
- if (mDozeHost.isPowerSaveActive()) {
- onPowerSave();
+ if (mUiModeManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_CAR
+ || mDozeHost.isPowerSaveActive()
+ || !mDozeHost.isProvisioned()) {
+ mMachine.requestState(DozeMachine.State.FINISH);
}
}
@@ -355,7 +346,7 @@
requestPulse(DozeLog.PULSE_REASON_INTENT, false /* performedProxCheck */);
}
if (UiModeManager.ACTION_ENTER_CAR_MODE.equals(intent.getAction())) {
- onCarMode();
+ mMachine.requestState(DozeMachine.State.FINISH);
}
if (Intent.ACTION_USER_SWITCHED.equals(intent.getAction())) {
mDozeSensors.onUserSwitched();
@@ -391,7 +382,7 @@
@Override
public void onPowerSaveChanged(boolean active) {
if (active) {
- onPowerSave();
+ mMachine.requestState(DozeMachine.State.FINISH);
}
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
index 1a8a474..31d41ac 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialog.java
@@ -682,10 +682,14 @@
/** {@inheritDoc} */
public void onClick(DialogInterface dialog, int which) {
- if (!(mAdapter.getItem(which) instanceof SilentModeTriStateAction)) {
+ Action item = mAdapter.getItem(which);
+ if ((item instanceof PowerAction)
+ || (item instanceof RestartAction)) {
+ if (mDialog != null) mDialog.fadeOut();
+ } else if (!(item instanceof SilentModeTriStateAction)) {
dialog.dismiss();
}
- mAdapter.getItem(which).onPress();
+ item.onPress();
}
/**
@@ -1321,6 +1325,17 @@
.start();
}
+ public void fadeOut() {
+ mHardwareLayout.setTranslationX(0);
+ mHardwareLayout.setAlpha(1);
+ mListView.animate()
+ .alpha(0)
+ .translationX(getAnimTranslation())
+ .setDuration(300)
+ .setInterpolator(new LogAccelerateInterpolator())
+ .start();
+ }
+
private float getAnimTranslation() {
return getContext().getResources().getDimension(
com.android.systemui.R.dimen.global_actions_panel_width) / 2;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
index 17a0d33..77c3bfa 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSFactoryImpl.java
@@ -16,7 +16,9 @@
import android.content.Context;
import android.util.Log;
+import android.view.ContextThemeWrapper;
+import com.android.systemui.R;
import com.android.systemui.plugins.qs.*;
import com.android.systemui.plugins.qs.QSTileView;
import com.android.systemui.qs.external.CustomTile;
@@ -78,7 +80,7 @@
@Override
public QSTileView createTileView(QSTile tile, boolean collapsedView) {
- Context context = mHost.getContext();
+ Context context = new ContextThemeWrapper(mHost.getContext(), R.style.qs_theme);
QSIconView icon = tile.createTileView(context);
if (collapsedView) {
return new QSTileBaseView(context, icon, collapsedView);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java
index cdd0ebc..6659650 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java
@@ -144,22 +144,26 @@
}
public static void animateGrayScale(int fromColor, int toColor, ImageView iv) {
- final float fromAlpha = Color.alpha(fromColor);
- final float toAlpha = Color.alpha(toColor);
- final float fromChannel = Color.red(fromColor);
- final float toChannel = Color.red(toColor);
+ if (ValueAnimator.areAnimatorsEnabled()) {
+ final float fromAlpha = Color.alpha(fromColor);
+ final float toAlpha = Color.alpha(toColor);
+ final float fromChannel = Color.red(fromColor);
+ final float toChannel = Color.red(toColor);
- ValueAnimator anim = ValueAnimator.ofFloat(0, 1);
- anim.setDuration(QS_ANIM_LENGTH);
+ ValueAnimator anim = ValueAnimator.ofFloat(0, 1);
+ anim.setDuration(QS_ANIM_LENGTH);
+ anim.addUpdateListener(animation -> {
+ float fraction = animation.getAnimatedFraction();
+ int alpha = (int) (fromAlpha + (toAlpha - fromAlpha) * fraction);
+ int channel = (int) (fromChannel + (toChannel - fromChannel) * fraction);
- anim.addUpdateListener(animation -> {
- float fraction = animation.getAnimatedFraction();
- int alpha = (int) (fromAlpha + (toAlpha - fromAlpha) * fraction);
- int channel = (int) (fromChannel + (toChannel - fromChannel) * fraction);
+ setTint(iv, Color.argb(alpha, channel, channel, channel));
+ });
- setTint(iv, Color.argb(alpha, channel, channel, channel));
- });
- anim.start();
+ anim.start();
+ } else {
+ setTint(iv, toColor);
+ }
}
public static void setTint(ImageView iv, int color) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
index 36677a4..14afbfa 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/BluetoothTile.java
@@ -30,6 +30,7 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.settingslib.Utils;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.systemui.Dependency;
import com.android.systemui.R;
@@ -271,7 +272,14 @@
int state = mController.getMaxConnectionState(device);
if (state == BluetoothProfile.STATE_CONNECTED) {
item.icon = R.drawable.ic_qs_bluetooth_connected;
- item.line2 = mContext.getString(R.string.quick_settings_connected);
+ int batteryLevel = device.getBatteryLevel();
+ if (batteryLevel != BluetoothDevice.BATTERY_LEVEL_UNKNOWN) {
+ item.line2 = mContext.getString(
+ R.string.quick_settings_connected_battery_level,
+ Utils.formatPercentage(batteryLevel));
+ } else {
+ item.line2 = mContext.getString(R.string.quick_settings_connected);
+ }
item.canDisconnect = true;
items.add(connectedDevices, item);
connectedDevices++;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
index 4de1214..fa16f8e 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsActivity.java
@@ -24,6 +24,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.net.Uri;
import android.os.Bundle;
@@ -42,9 +43,9 @@
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+import com.android.keyguard.LatencyTracker;
import com.android.systemui.DejankUtils;
import com.android.systemui.Interpolators;
-import com.android.keyguard.LatencyTracker;
import com.android.systemui.R;
import com.android.systemui.recents.events.EventBus;
import com.android.systemui.recents.events.activity.CancelEnterRecentsWindowAnimationEvent;
@@ -110,11 +111,10 @@
private RecentsPackageMonitor mPackageMonitor;
private Handler mHandler = new Handler();
private long mLastTabKeyEventTime;
- private int mLastDeviceOrientation = Configuration.ORIENTATION_UNDEFINED;
- private int mLastDisplayDensity;
private boolean mFinishedOnStartup;
private boolean mIgnoreAltTabRelease;
private boolean mIsVisible;
+ private Configuration mLastConfig;
// Top level views
private RecentsView mRecentsView;
@@ -333,16 +333,11 @@
setContentView(R.layout.recents);
takeKeyEvents(true);
mRecentsView = findViewById(R.id.recents_view);
- mRecentsView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
- View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
- View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
mScrimViews = new SystemBarScrimViews(this);
getWindow().getAttributes().privateFlags |=
WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY;
- Configuration appConfiguration = Utilities.getAppConfiguration(this);
- mLastDeviceOrientation = appConfiguration.orientation;
- mLastDisplayDensity = appConfiguration.densityDpi;
+ mLastConfig = Utilities.getAppConfiguration(this);
mFocusTimerDuration = getResources().getInteger(R.integer.recents_auto_advance_duration);
mIterateTrigger = new DozeTrigger(mFocusTimerDuration, new Runnable() {
@Override
@@ -485,10 +480,15 @@
Configuration newDeviceConfiguration = Utilities.getAppConfiguration(this);
int numStackTasks = mRecentsView.getStack().getStackTaskCount();
EventBus.getDefault().send(new ConfigurationChangedEvent(false /* fromMultiWindow */,
- mLastDeviceOrientation != newDeviceConfiguration.orientation,
- mLastDisplayDensity != newDeviceConfiguration.densityDpi, numStackTasks > 0));
- mLastDeviceOrientation = newDeviceConfiguration.orientation;
- mLastDisplayDensity = newDeviceConfiguration.densityDpi;
+ mLastConfig.orientation != newDeviceConfiguration.orientation,
+ mLastConfig.densityDpi != newDeviceConfiguration.densityDpi, numStackTasks > 0));
+
+ int configDiff = mLastConfig.updateFrom(newDeviceConfiguration);
+
+ // Recreate activity if an overlay was enabled/disabled
+ if ((configDiff & ActivityInfo.CONFIG_ASSETS_PATHS) != 0) {
+ recreate();
+ }
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
index 521157d..d74970f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/ScreenPinningRequest.java
@@ -43,14 +43,14 @@
import android.widget.TextView;
import com.android.systemui.R;
+import com.android.systemui.util.leak.RotationUtils;
import java.util.ArrayList;
-public class ScreenPinningRequest implements View.OnClickListener {
+import static com.android.systemui.util.leak.RotationUtils.ROTATION_LANDSCAPE;
+import static com.android.systemui.util.leak.RotationUtils.ROTATION_SEASCAPE;
- private static final int ROTATION_NONE = 0;
- private static final int ROTATION_LANDSCAPE = 1;
- private static final int ROTATION_SEASCAPE = 2;
+public class ScreenPinningRequest implements View.OnClickListener {
private final Context mContext;
@@ -160,7 +160,7 @@
DisplayMetrics metrics = new DisplayMetrics();
mWindowManager.getDefaultDisplay().getMetrics(metrics);
float density = metrics.density;
- int rotation = getRotation(mContext);
+ int rotation = RotationUtils.getRotation(mContext);
inflateView(rotation);
int bgColor = mContext.getColor(
@@ -202,19 +202,6 @@
mContext.registerReceiver(mReceiver, filter);
}
- private int getRotation(Context context) {
- Configuration config = mContext.getResources().getConfiguration();
- int rot = context.getDisplay().getRotation();
- if (config.smallestScreenWidthDp < 600) {
- if (rot == Surface.ROTATION_90) {
- return ROTATION_LANDSCAPE;
- } else if (rot == Surface.ROTATION_270) {
- return ROTATION_SEASCAPE;
- }
- }
- return ROTATION_NONE;
- }
-
private void inflateView(int rotation) {
// We only want this landscape orientation on <600dp, so rather than handle
// resource overlay for -land and -sw600dp-land, just inflate this
@@ -287,14 +274,14 @@
protected void onConfigurationChanged() {
removeAllViews();
- inflateView(getRotation(mContext));
+ inflateView(RotationUtils.getRotation(mContext));
}
private final Runnable mUpdateLayoutRunnable = new Runnable() {
@Override
public void run() {
if (mLayout != null && mLayout.getParent() != null) {
- mLayout.setLayoutParams(getRequestLayoutParams(getRotation(mContext)));
+ mLayout.setLayoutParams(getRequestLayoutParams(RotationUtils.getRotation(mContext)));
}
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java
index 21dfe8c..968b77f 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsTransitionHelper.java
@@ -400,8 +400,8 @@
view.draw(c);
}
node.end(c);
- return ThreadedRenderer.createHardwareBitmap(node, bufferWidth, bufferHeight)
- .createGraphicBufferHandle();
+ Bitmap hwBitmap = ThreadedRenderer.createHardwareBitmap(node, bufferWidth, bufferHeight);
+ return hwBitmap.createGraphicBufferHandle();
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
index 9ca756c..c1c41be0 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -151,6 +151,12 @@
mColorExtractor = Dependency.get(SysuiColorExtractor.class);
LayoutInflater inflater = LayoutInflater.from(context);
+
+ mEmptyView = (TextView) inflater.inflate(R.layout.recents_empty, this, false);
+ addView(mEmptyView);
+
+ boolean usingDarkText =
+ Color.luminance(mEmptyView.getTextColors().getDefaultColor()) < 0.5f;
if (RecentsDebugFlags.Static.EnableStackActionButton) {
mStackActionButton = (TextView) inflater.inflate(R.layout.recents_stack_action_button,
this, false);
@@ -160,10 +166,21 @@
EventBus.getDefault().send(new DismissAllTaskViewsEvent());
}
});
+ // Disable black shadow if text color is already dark.
+ if (usingDarkText) {
+ mStackActionButton.setShadowLayer(0, 0, 0, 0);
+ }
addView(mStackActionButton);
}
- mEmptyView = (TextView) inflater.inflate(R.layout.recents_empty, this, false);
- addView(mEmptyView);
+
+ // Let's also require dark status and nav bars if the text is dark
+ int systemBarsStyle = usingDarkText ? View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR |
+ View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR : 0;
+
+ setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE |
+ View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN |
+ View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION |
+ systemBarsStyle);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java
index 674a61c..df1ffda 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardAffordanceHelper.java
@@ -168,8 +168,7 @@
distance = mTranslationOnDown + distance;
distance = Math.max(0, distance);
}
- setTranslation(distance, false /* isReset */, false /* animateReset */,
- false /* force */);
+ setTranslation(distance, false /* isReset */, false /* animateReset */);
}
break;
@@ -374,12 +373,11 @@
targetView.finishAnimation(velocity, mAnimationEndRunnable);
}
- private void setTranslation(float translation, boolean isReset, boolean animateReset,
- boolean force) {
+ private void setTranslation(float translation, boolean isReset, boolean animateReset) {
translation = rightSwipePossible() ? translation : Math.max(0, translation);
translation = leftSwipePossible() ? translation : Math.min(0, translation);
float absTranslation = Math.abs(translation);
- if (translation != mTranslation || isReset || force) {
+ if (translation != mTranslation || isReset) {
KeyguardAffordanceView targetView = translation > 0 ? mLeftIcon : mRightIcon;
KeyguardAffordanceView otherView = translation > 0 ? mRightIcon : mLeftIcon;
float alpha = absTranslation / getMinTranslationAmount();
@@ -394,15 +392,15 @@
boolean slowAnimation = isReset && isBelowFalsingThreshold();
if (!isReset) {
updateIcon(targetView, radius, alpha + fadeOutAlpha * targetView.getRestingAlpha(),
- false, false, force, false);
+ false, false, false, false);
} else {
updateIcon(targetView, 0.0f, fadeOutAlpha * targetView.getRestingAlpha(),
- animateIcons, slowAnimation, force, forceNoCircleAnimation);
+ animateIcons, slowAnimation, true /* isReset */, forceNoCircleAnimation);
}
updateIcon(otherView, 0.0f, fadeOutAlpha * otherView.getRestingAlpha(),
- animateIcons, slowAnimation, force, forceNoCircleAnimation);
+ animateIcons, slowAnimation, isReset, forceNoCircleAnimation);
updateIcon(mCenterIcon, 0.0f, fadeOutAlpha * mCenterIcon.getRestingAlpha(),
- animateIcons, slowAnimation, force, forceNoCircleAnimation);
+ animateIcons, slowAnimation, isReset, forceNoCircleAnimation);
mTranslation = translation;
}
@@ -510,12 +508,8 @@
}
public void reset(boolean animate) {
- reset(animate, false /* force */);
- }
-
- public void reset(boolean animate, boolean force) {
cancelAnimation();
- setTranslation(0.0f, true, animate, force);
+ setTranslation(0.0f, true /* isReset */, animate);
mMotionCancelled = true;
if (mSwipingInProgress) {
mCallback.onSwipingAborted();
@@ -523,10 +517,6 @@
}
}
- public void resetImmediately() {
- reset(false /* animate */, true /* force */);
- }
-
public boolean isSwipingInProgress() {
return mSwipingInProgress;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
index a4dfcd8..e9aa6d2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
@@ -254,6 +254,7 @@
mFlashlightController = Dependency.get(FlashlightController.class);
mAccessibilityController = Dependency.get(AccessibilityController.class);
mAssistManager = Dependency.get(AssistManager.class);
+ mLockIcon.setAccessibilityController(mAccessibilityController);
updateLeftAffordance();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index 5116414..bbcf6c7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -2573,7 +2573,7 @@
public void setTouchDisabled(boolean disabled) {
super.setTouchDisabled(disabled);
if (disabled && mAffordanceHelper.isSwipingInProgress() && !mIsLaunchTransitionRunning) {
- mAffordanceHelper.resetImmediately();
+ mAffordanceHelper.reset(false /* animate */);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index bcba789..2217133 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -1150,7 +1150,6 @@
ExtensionFragmentListener.attachExtensonToFragment(container, QS.TAG, R.id.qs_frame,
Dependency.get(ExtensionController.class).newExtension(QS.class)
.withPlugin(QS.class)
- .withUiMode(UI_MODE_TYPE_CAR, () -> new QSFragment())
.withDefault(() -> new QSFragment())
.build());
final QSTileHost qsh = SystemUIFactory.getInstance().createQSTileHost(mContext, this,
@@ -1318,6 +1317,9 @@
.setStatusBarKeyguardViewManager(mStatusBarKeyguardViewManager);
mKeyguardIndicationController.setVisible(mState == StatusBarState.KEYGUARD);
mKeyguardIndicationController.setDozing(mDozing);
+ if (mBrightnessMirrorController != null) {
+ mBrightnessMirrorController.onOverlayChanged();
+ }
}
protected void reevaluateStyles() {
@@ -3757,7 +3759,7 @@
// SystemUIService notifies SystemBars of configuration changes, which then calls down here
@Override
- protected void onConfigurationChanged(Configuration newConfig) {
+ public void onConfigChanged(Configuration newConfig) {
updateResources();
updateDisplaySize(); // populates mDisplayMetrics
@@ -4207,7 +4209,7 @@
}
if (shouldBeKeyguard) {
showKeyguardImpl();
- } else if (!shouldBeKeyguard && mIsKeyguard) {
+ } else {
return hideKeyguardImpl();
}
return false;
@@ -5369,6 +5371,12 @@
}
@Override
+ public boolean isProvisioned() {
+ return mDeviceProvisionedController.isDeviceProvisioned()
+ && mDeviceProvisionedController.isCurrentUserSetup();
+ }
+
+ @Override
public void startPendingIntentDismissingKeyguard(PendingIntent intent) {
StatusBar.this.startPendingIntentDismissingKeyguard(intent);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java
index ad406c7..ab55b23 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BrightnessMirrorController.java
@@ -84,7 +84,6 @@
.setInterpolator(Interpolators.ALPHA_IN);
}
-
public void setLocation(View original) {
original.getLocationInWindow(mInt2Cache);
@@ -115,7 +114,15 @@
mBrightnessMirror.setLayoutParams(lp);
}
+ public void onOverlayChanged() {
+ reinflate();
+ }
+
public void onDensityOrFontScaleChanged() {
+ reinflate();
+ }
+
+ private void reinflate() {
int index = mStatusBarWindow.indexOfChild(mBrightnessMirror);
mStatusBarWindow.removeView(mBrightnessMirror);
mBrightnessMirror = LayoutInflater.from(mBrightnessMirror.getContext()).inflate(
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 2981b39..74523e2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -2589,9 +2589,6 @@
}
updateAnimationState(false, child);
- // Make sure the clipRect we might have set is removed
- expandableView.setClipTopAmount(0);
-
focusNextViewIfFocused(child);
}
@@ -3038,10 +3035,6 @@
private void generateChildRemovalEvents() {
for (View child : mChildrenToRemoveAnimated) {
boolean childWasSwipedOut = mSwipedOutViews.contains(child);
- int animationType = childWasSwipedOut
- ? AnimationEvent.ANIMATION_TYPE_REMOVE_SWIPED_OUT
- : AnimationEvent.ANIMATION_TYPE_REMOVE;
- AnimationEvent event = new AnimationEvent(child, animationType);
// we need to know the view after this one
float removedTranslation = child.getTranslationY();
@@ -3052,7 +3045,16 @@
removedTranslation = row.getTranslationWhenRemoved();
ignoreChildren = false;
}
+ childWasSwipedOut |= Math.abs(row.getTranslation()) == row.getWidth();
}
+ if (!childWasSwipedOut) {
+ Rect clipBounds = child.getClipBounds();
+ childWasSwipedOut = clipBounds.height() == 0;
+ }
+ int animationType = childWasSwipedOut
+ ? AnimationEvent.ANIMATION_TYPE_REMOVE_SWIPED_OUT
+ : AnimationEvent.ANIMATION_TYPE_REMOVE;
+ AnimationEvent event = new AnimationEvent(child, animationType);
event.viewAfterChangingView = getFirstChildBelowTranlsationY(removedTranslation,
ignoreChildren);
mAnimationEvents.add(event);
@@ -3360,15 +3362,29 @@
if (!mIsExpanded) {
setOwnScrollY(0);
mStatusBar.resetUserExpandedStates();
+ clearTemporaryViews();
+ clearUserLockedViews();
+ }
+ }
- // lets make sure nothing is in the overlay / transient anymore
- clearTemporaryViews(this);
- for (int i = 0; i < getChildCount(); i++) {
- ExpandableView child = (ExpandableView) getChildAt(i);
- if (child instanceof ExpandableNotificationRow) {
- ExpandableNotificationRow row = (ExpandableNotificationRow) child;
- clearTemporaryViews(row.getChildrenContainer());
- }
+ private void clearUserLockedViews() {
+ for (int i = 0; i < getChildCount(); i++) {
+ ExpandableView child = (ExpandableView) getChildAt(i);
+ if (child instanceof ExpandableNotificationRow) {
+ ExpandableNotificationRow row = (ExpandableNotificationRow) child;
+ row.setUserLocked(false);
+ }
+ }
+ }
+
+ private void clearTemporaryViews() {
+ // lets make sure nothing is in the overlay / transient anymore
+ clearTemporaryViews(this);
+ for (int i = 0; i < getChildCount(); i++) {
+ ExpandableView child = (ExpandableView) getChildAt(i);
+ if (child instanceof ExpandableNotificationRow) {
+ ExpandableNotificationRow row = (ExpandableNotificationRow) child;
+ clearTemporaryViews(row.getChildrenContainer());
}
}
}
@@ -3403,6 +3419,7 @@
if (changed) {
if (!mIsExpanded) {
mGroupManager.collapseAllGroups();
+ mExpandHelper.cancelImmediately();
}
updateNotificationAnimationStates();
updateChronometers();
diff --git a/packages/SystemUI/src/com/android/systemui/util/leak/RotationUtils.java b/packages/SystemUI/src/com/android/systemui/util/leak/RotationUtils.java
new file mode 100644
index 0000000..ad20900
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/leak/RotationUtils.java
@@ -0,0 +1,39 @@
+/*
+ * 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.util.leak;
+
+import android.content.Context;
+import android.content.res.Configuration;
+import android.view.Surface;
+
+public class RotationUtils {
+
+ public static final int ROTATION_NONE = 0;
+ public static final int ROTATION_LANDSCAPE = 1;
+ public static final int ROTATION_SEASCAPE = 2;
+
+ public static int getRotation(Context context) {
+ Configuration config = context.getResources().getConfiguration();
+ int rot = context.getDisplay().getRotation();
+ if (config.smallestScreenWidthDp < 600) {
+ if (rot == Surface.ROTATION_90) {
+ return ROTATION_LANDSCAPE;
+ } else if (rot == Surface.ROTATION_270) {
+ return ROTATION_SEASCAPE;
+ }
+ }
+ return ROTATION_NONE;
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeHostFake.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeHostFake.java
index 2345110..8ff9fa9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeHostFake.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeHostFake.java
@@ -72,6 +72,11 @@
}
@Override
+ public boolean isProvisioned() {
+ return false;
+ }
+
+ @Override
public void startPendingIntentDismissingKeyguard(PendingIntent intent) {
throw new RuntimeException("not implemented");
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
index 0937ce2..c0de004 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/AutoTileManagerTest.java
@@ -19,12 +19,13 @@
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
+import android.support.test.filters.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper.RunWithLooper;
+
import com.android.internal.app.NightDisplayController;
import com.android.systemui.Prefs;
import com.android.systemui.Prefs.Key;
-
-import android.support.test.filters.SmallTest;
-import android.testing.AndroidTestingRunner;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.qs.QSTileHost;
@@ -35,6 +36,7 @@
import org.mockito.Mockito;
@RunWith(AndroidTestingRunner.class)
+@RunWithLooper
@SmallTest
public class AutoTileManagerTest extends SysuiTestCase {
diff --git a/packages/overlays/SysuiDarkThemeOverlay/AndroidManifest.xml b/packages/overlays/SysuiDarkThemeOverlay/AndroidManifest.xml
index ba1c91c..8b6ee2b 100644
--- a/packages/overlays/SysuiDarkThemeOverlay/AndroidManifest.xml
+++ b/packages/overlays/SysuiDarkThemeOverlay/AndroidManifest.xml
@@ -2,7 +2,7 @@
package="com.android.systemui.theme.dark"
android:versionCode="1"
android:versionName="1.0">
- <overlay android:targetPackage="android" android:priority="1"/>
+ <overlay android:targetPackage="com.android.systemui" android:priority="1"/>
<application android:label="@string/sysui_overlay_dark" android:hasCode="false"/>
</manifest>
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values/styles.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values/styles.xml
new file mode 100644
index 0000000..0c1b0ef
--- /dev/null
+++ b/packages/overlays/SysuiDarkThemeOverlay/res/values/styles.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <style name="qs_base" parent="android:Theme.DeviceDefault">
+ <item name="android:colorPrimary">@*android:color/primary_device_default_settings</item>
+ <item name="android:colorPrimaryDark">@*android:color/primary_dark_device_default_settings</item>
+ <item name="android:colorSecondary">@*android:color/secondary_device_default_settings</item>
+ <item name="android:colorAccent">@*android:color/accent_device_default_dark</item>
+ <item name="android:colorControlNormal">?android:attr/textColorPrimary</item>
+ <item name="android:colorBackgroundFloating">#000</item>
+ </style>
+</resources>
\ No newline at end of file
diff --git a/packages/overlays/SysuiDarkThemeOverlay/res/values/themes_device_defaults.xml b/packages/overlays/SysuiDarkThemeOverlay/res/values/themes_device_defaults.xml
deleted file mode 100644
index 7e2b955..0000000
--- a/packages/overlays/SysuiDarkThemeOverlay/res/values/themes_device_defaults.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<resources>
- <style name="Theme.DeviceDefault.QuickSettings" parent="android:Theme.DeviceDefault">
- <item name="android:colorPrimary">@*android:color/primary_device_default_settings</item>
- <item name="android:colorPrimaryDark">@*android:color/primary_dark_device_default_settings</item>
- <!-- textColorPrimaryInverse is used on the lock screen and this means that we can't just
- invert text colors otherwise we won't have contrast on the keyguard -->
- <item name="android:textColorPrimaryInverse">@*android:color/primary_text_material_dark</item>
- <!-- same for textColorSecondaryInverse -->
- <item name="android:textColorSecondaryInverse">@*android:color/secondary_text_material_dark</item>
- <item name="android:colorSecondary">@*android:color/secondary_device_default_settings</item>
- <item name="android:colorAccent">@*android:color/accent_device_default_dark</item>
- <item name="android:colorControlNormal">?android:attr/textColorPrimary</item>
- <item name="android:colorBackgroundFloating">#000</item>
- </style>
-</resources>
\ No newline at end of file
diff --git a/packages/overlays/SysuiLightWallpaperThemeOverlay/AndroidManifest.xml b/packages/overlays/SysuiLightWallpaperThemeOverlay/AndroidManifest.xml
index 1745b4c..0a8749c 100644
--- a/packages/overlays/SysuiLightWallpaperThemeOverlay/AndroidManifest.xml
+++ b/packages/overlays/SysuiLightWallpaperThemeOverlay/AndroidManifest.xml
@@ -2,7 +2,7 @@
package="com.android.systemui.theme.lightwallpaper"
android:versionCode="1"
android:versionName="1.0">
- <overlay android:targetPackage="android" android:priority="2"/>
+ <overlay android:targetPackage="com.android.systemui" android:priority="2"/>
<application android:label="@string/sysui_overlay_light" android:hasCode="false"/>
</manifest>
diff --git a/packages/overlays/SysuiLightWallpaperThemeOverlay/res/values/styles.xml b/packages/overlays/SysuiLightWallpaperThemeOverlay/res/values/styles.xml
new file mode 100644
index 0000000..53912b5
--- /dev/null
+++ b/packages/overlays/SysuiLightWallpaperThemeOverlay/res/values/styles.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <style name="systemui_base" parent="@*android:style/Theme.DeviceDefault.QuickSettings">
+ <item name="android:textColorPrimaryInverse">@*android:color/primary_text_material_light</item>
+ <item name="android:textColorSecondaryInverse">@*android:color/secondary_text_material_light</item>
+ </style>
+
+ <style name="RecentsBase" parent="@android:style/Theme.Material">
+ <item name="android:textColorPrimaryInverse">@*android:color/primary_text_material_light</item>
+ <item name="android:textColorSecondaryInverse">@*android:color/secondary_text_material_light</item>
+ </style>
+</resources>
\ No newline at end of file
diff --git a/packages/overlays/SysuiLightWallpaperThemeOverlay/res/values/themes_device_defaults.xml b/packages/overlays/SysuiLightWallpaperThemeOverlay/res/values/themes_device_defaults.xml
deleted file mode 100644
index 877ebf8..0000000
--- a/packages/overlays/SysuiLightWallpaperThemeOverlay/res/values/themes_device_defaults.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<resources>
- <style name="Theme.DeviceDefault.QuickSettings" parent="android:Theme.DeviceDefault.Light">
- <item name="android:textColorPrimaryInverse">@*android:color/primary_text_material_light</item>
- <item name="android:textColorSecondaryInverse">@*android:color/secondary_text_material_light</item>
- </style>
-</resources>
\ No newline at end of file
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 224845c..c4e0e52 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -210,13 +210,6 @@
// See Settings.Secure.CONNECTIVITY_RELEASE_PENDING_INTENT_DELAY_MS
private final int mReleasePendingIntentDelayMs;
- // Driver specific constants used to select packets received via
- // WiFi that caused the phone to exit sleep state. Currently there
- // is only one kernel implementation so we can get away with
- // constants.
- private static final int mWakeupPacketMark = 0x80000000;
- private static final int mWakeupPacketMask = 0x80000000;
-
private MockableSystemProperties mSystemProperties;
private Tethering mTethering;
@@ -2282,6 +2275,10 @@
notifyNetworkCallbacks(nai, ConnectivityManager.CALLBACK_LOST);
mKeepaliveTracker.handleStopAllKeepalives(nai,
ConnectivityManager.PacketKeepalive.ERROR_INVALID_NETWORK);
+ for (String iface : nai.linkProperties.getAllInterfaceNames()) {
+ // Disable wakeup packet monitoring for each interface.
+ wakeupModifyInterface(iface, nai.networkCapabilities, false);
+ }
nai.networkMonitor.sendMessage(NetworkMonitor.CMD_NETWORK_DISCONNECTED);
mNetworkAgentInfos.remove(msg.replyTo);
updateClat(null, nai.linkProperties, nai);
@@ -4401,22 +4398,35 @@
}
}
- private void wakeupAddInterface(String iface, NetworkCapabilities caps) throws RemoteException {
+ private void wakeupModifyInterface(String iface, NetworkCapabilities caps, boolean add) {
// Marks are only available on WiFi interaces. Checking for
// marks on unsupported interfaces is harmless.
if (!caps.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) {
return;
}
- mNetd.getNetdService().wakeupAddInterface(
- iface, "iface:" + iface, mWakeupPacketMark, mWakeupPacketMask);
- }
- private void wakeupDelInterface(String iface, NetworkCapabilities caps) throws RemoteException {
- if (!caps.hasTransport(NetworkCapabilities.TRANSPORT_WIFI)) {
+ int mark = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_networkWakeupPacketMark);
+ int mask = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_networkWakeupPacketMask);
+
+ // Mask/mark of zero will not detect anything interesting.
+ // Don't install rules unless both values are nonzero.
+ if (mark == 0 || mask == 0) {
return;
}
- mNetd.getNetdService().wakeupDelInterface(
- iface, "iface:" + iface, mWakeupPacketMark, mWakeupPacketMask);
+
+ final String prefix = "iface:" + iface;
+ try {
+ if (add) {
+ mNetd.getNetdService().wakeupAddInterface(iface, prefix, mark, mask);
+ } else {
+ mNetd.getNetdService().wakeupDelInterface(iface, prefix, mark, mask);
+ }
+ } catch (Exception e) {
+ loge("Exception modifying wakeup packet monitoring: " + e);
+ }
+
}
private void updateInterfaces(LinkProperties newLp, LinkProperties oldLp, int netId,
@@ -4431,7 +4441,7 @@
try {
if (DBG) log("Adding iface " + iface + " to network " + netId);
mNetd.addInterfaceToNetwork(iface, netId);
- wakeupAddInterface(iface, caps);
+ wakeupModifyInterface(iface, caps, true);
} catch (Exception e) {
loge("Exception adding interface: " + e);
}
@@ -4439,8 +4449,8 @@
for (String iface : interfaceDiff.removed) {
try {
if (DBG) log("Removing iface " + iface + " from network " + netId);
+ wakeupModifyInterface(iface, caps, false);
mNetd.removeInterfaceFromNetwork(iface, netId);
- wakeupDelInterface(iface, caps);
} catch (Exception e) {
loge("Exception removing interface: " + e);
}
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 9cde9859..0295f18 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -2882,8 +2882,9 @@
} else {
// If a new task is being launched, then mark the existing top activity as
// supporting picture-in-picture while pausing
- if (focusedTopActivity != null &&
- focusedTopActivity.getStack().getStackId() != PINNED_STACK_ID) {
+ if (focusedTopActivity != null
+ && focusedTopActivity.getStackId() != PINNED_STACK_ID
+ && r.getStackId() != ASSISTANT_STACK_ID) {
focusedTopActivity.supportsPictureInPictureWhilePausing = true;
}
transit = TRANSIT_TASK_OPEN;
@@ -4549,7 +4550,8 @@
}
// If a new task is moved to the front, then mark the existing top activity as supporting
// picture-in-picture while paused
- if (topActivity != null && topActivity.getStack().getStackId() != PINNED_STACK_ID) {
+ if (topActivity != null && topActivity.getStackId() != PINNED_STACK_ID
+ && tr.getStackId() != ASSISTANT_STACK_ID) {
topActivity.supportsPictureInPictureWhilePausing = true;
}
diff --git a/services/core/java/com/android/server/connectivity/Tethering.java b/services/core/java/com/android/server/connectivity/Tethering.java
index e2956dd..b657335 100644
--- a/services/core/java/com/android/server/connectivity/Tethering.java
+++ b/services/core/java/com/android/server/connectivity/Tethering.java
@@ -16,6 +16,7 @@
package com.android.server.connectivity;
+import static android.hardware.usb.UsbManager.USB_CONFIGURED;
import static android.hardware.usb.UsbManager.USB_CONNECTED;
import static android.hardware.usb.UsbManager.USB_FUNCTION_RNDIS;
import static android.net.ConnectivityManager.getNetworkTypeName;
@@ -703,7 +704,7 @@
private void showTetheredNotification(int id) {
NotificationManager notificationManager =
- (NotificationManager)mContext.getSystemService(Context.NOTIFICATION_SERVICE);
+ (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
if (notificationManager == null) {
return;
}
@@ -764,7 +765,7 @@
private void clearTetheredNotification() {
NotificationManager notificationManager =
- (NotificationManager)mContext.getSystemService(Context.NOTIFICATION_SERVICE);
+ (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
if (notificationManager != null && mLastNotificationId != 0) {
notificationManager.cancelAsUser(null, mLastNotificationId,
UserHandle.ALL);
@@ -803,11 +804,37 @@
private void handleUsbAction(Intent intent) {
final boolean usbConnected = intent.getBooleanExtra(USB_CONNECTED, false);
+ final boolean usbConfigured = intent.getBooleanExtra(USB_CONFIGURED, false);
final boolean rndisEnabled = intent.getBooleanExtra(USB_FUNCTION_RNDIS, false);
+
+ mLog.log(String.format("USB bcast connected:%s configured:%s rndis:%s",
+ usbConnected, usbConfigured, rndisEnabled));
+
+ // There are three types of ACTION_USB_STATE:
+ //
+ // - DISCONNECTED (USB_CONNECTED and USB_CONFIGURED are 0)
+ // Meaning: USB connection has ended either because of
+ // software reset or hard unplug.
+ //
+ // - CONNECTED (USB_CONNECTED is 1, USB_CONFIGURED is 0)
+ // Meaning: the first stage of USB protocol handshake has
+ // occurred but it is not complete.
+ //
+ // - CONFIGURED (USB_CONNECTED and USB_CONFIGURED are 1)
+ // Meaning: the USB handshake is completely done and all the
+ // functions are ready to use.
+ //
+ // For more explanation, see b/62552150 .
+ if (usbConnected && !usbConfigured) {
+ // Nothing for us to do here.
+ // TODO: consider ignoring DISCONNECTED broadcasts as well.
+ return;
+ }
+
synchronized (Tethering.this.mPublicSync) {
mRndisEnabled = rndisEnabled;
// start tethering if we have a request pending
- if (usbConnected && mRndisEnabled && mUsbTetherRequested) {
+ if (usbConfigured && mRndisEnabled && mUsbTetherRequested) {
tetherMatchingInterfaces(
IControlsTethering.STATE_TETHERED,
ConnectivityManager.TETHERING_USB);
@@ -983,7 +1010,7 @@
public int setUsbTethering(boolean enable) {
if (VDBG) Log.d(TAG, "setUsbTethering(" + enable + ")");
- UsbManager usbManager = mContext.getSystemService(UsbManager.class);
+ UsbManager usbManager = (UsbManager) mContext.getSystemService(Context.USB_SERVICE);
synchronized (mPublicSync) {
if (enable) {
diff --git a/services/core/java/com/android/server/connectivity/tethering/OffloadController.java b/services/core/java/com/android/server/connectivity/tethering/OffloadController.java
index 78487b7..20ec206 100644
--- a/services/core/java/com/android/server/connectivity/tethering/OffloadController.java
+++ b/services/core/java/com/android/server/connectivity/tethering/OffloadController.java
@@ -58,7 +58,12 @@
}
public void start() {
- if (isOffloadDisabled() || started()) return;
+ if (started()) return;
+
+ if (isOffloadDisabled()) {
+ mLog.i("tethering offload disabled");
+ return;
+ }
if (!mConfigInitialized) {
mConfigInitialized = mHwInterface.initOffloadConfig();
diff --git a/services/core/java/com/android/server/connectivity/tethering/OffloadHardwareInterface.java b/services/core/java/com/android/server/connectivity/tethering/OffloadHardwareInterface.java
index 1fc1684..09fd96b 100644
--- a/services/core/java/com/android/server/connectivity/tethering/OffloadHardwareInterface.java
+++ b/services/core/java/com/android/server/connectivity/tethering/OffloadHardwareInterface.java
@@ -76,6 +76,10 @@
}
}
+ final String logmsg = String.format("initOffloadControl(%s)",
+ (controlCb == null) ? "null"
+ : "0x" + Integer.toHexString(System.identityHashCode(controlCb)));
+
mTetheringOffloadCallback = new TetheringOffloadCallback(mHandler, mControlCallback);
final CbResults results = new CbResults();
try {
@@ -86,11 +90,11 @@
results.errMsg = errMsg;
});
} catch (RemoteException e) {
- mLog.e("failed to initOffload: " + e);
+ record(logmsg, e);
return false;
}
- if (!results.success) mLog.e("initOffload failed: " + results.errMsg);
+ record(logmsg, results);
return results.success;
}
@@ -108,14 +112,18 @@
mOffloadControl = null;
mTetheringOffloadCallback = null;
mControlCallback = null;
+ mLog.log("stopOffloadControl()");
}
public boolean setUpstreamParameters(
String iface, String v4addr, String v4gateway, ArrayList<String> v6gws) {
- iface = iface != null ? iface : NO_INTERFACE_NAME;
- v4addr = v4addr != null ? v4addr : NO_IPV4_ADDRESS;
- v4gateway = v4gateway != null ? v4gateway : NO_IPV4_GATEWAY;
- v6gws = v6gws != null ? v6gws : new ArrayList<>();
+ iface = (iface != null) ? iface : NO_INTERFACE_NAME;
+ v4addr = (v4addr != null) ? v4addr : NO_IPV4_ADDRESS;
+ v4gateway = (v4gateway != null) ? v4gateway : NO_IPV4_GATEWAY;
+ v6gws = (v6gws != null) ? v6gws : new ArrayList<>();
+
+ final String logmsg = String.format("setUpstreamParameters(%s, %s, %s, [%s])",
+ iface, v4addr, v4gateway, String.join(",", v6gws));
final CbResults results = new CbResults();
try {
@@ -126,14 +134,27 @@
results.errMsg = errMsg;
});
} catch (RemoteException e) {
- mLog.e("failed to setUpstreamParameters: " + e);
+ record(logmsg, e);
return false;
}
- if (!results.success) mLog.e("setUpstreamParameters failed: " + results.errMsg);
+ record(logmsg, results);
return results.success;
}
+ private void record(String msg, Throwable t) {
+ mLog.e(msg + " -> exception: " + t);
+ }
+
+ private void record(String msg, CbResults results) {
+ final String logmsg = msg + " -> " + results;
+ if (!results.success) {
+ mLog.e(logmsg);
+ } else {
+ mLog.log(logmsg);
+ }
+ }
+
private static class TetheringOffloadCallback extends ITetheringOffloadCallback.Stub {
public final Handler handler;
public final ControlCallback controlCb;
@@ -162,5 +183,13 @@
private static class CbResults {
boolean success;
String errMsg;
+
+ public String toString() {
+ if (success) {
+ return "ok";
+ } else {
+ return "fail: " + errMsg;
+ }
+ }
}
}
diff --git a/services/core/java/com/android/server/policy/ImmersiveModeConfirmation.java b/services/core/java/com/android/server/policy/ImmersiveModeConfirmation.java
index ebb9450..c6ec287 100644
--- a/services/core/java/com/android/server/policy/ImmersiveModeConfirmation.java
+++ b/services/core/java/com/android/server/policy/ImmersiveModeConfirmation.java
@@ -19,6 +19,7 @@
import android.animation.ArgbEvaluator;
import android.animation.ValueAnimator;
import android.app.ActivityManager;
+import android.app.ActivityThread;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -79,7 +80,7 @@
boolean mVrModeEnabled = false;
public ImmersiveModeConfirmation(Context context) {
- mContext = context;
+ mContext = ActivityThread.currentActivityThread().getSystemUiContext();
mHandler = new H();
mShowDelayMs = getNavBarExitDuration() * 3;
mPanicThresholdMs = context.getResources()
diff --git a/services/core/java/com/android/server/power/ShutdownThread.java b/services/core/java/com/android/server/power/ShutdownThread.java
index 02f2afc..56612ad 100644
--- a/services/core/java/com/android/server/power/ShutdownThread.java
+++ b/services/core/java/com/android/server/power/ShutdownThread.java
@@ -23,14 +23,17 @@
import android.app.ProgressDialog;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.IBluetoothManager;
-import android.media.AudioAttributes;
-import android.nfc.NfcAdapter;
-import android.nfc.INfcAdapter;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.om.IOverlayManager;
+import android.graphics.Color;
+import android.graphics.drawable.ColorDrawable;
+import android.media.AudioAttributes;
+import android.nfc.INfcAdapter;
+import android.nfc.NfcAdapter;
import android.os.FileUtils;
import android.os.Handler;
import android.os.PowerManager;
@@ -39,24 +42,21 @@
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.SystemProperties;
+import android.os.SystemVibrator;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.Vibrator;
-import android.os.SystemVibrator;
-import android.os.storage.IStorageShutdownObserver;
import android.os.storage.IStorageManager;
-import android.system.ErrnoException;
-import android.system.Os;
-
+import android.os.storage.IStorageShutdownObserver;
+import android.util.Log;
+import android.view.ViewGroup;
+import android.view.WindowManager;
+import android.widget.ProgressBar;
+import android.widget.TextView;
import com.android.internal.telephony.ITelephony;
import com.android.server.pm.PackageManagerService;
-import android.util.Log;
-import android.view.WindowManager;
-
-import java.io.BufferedReader;
import java.io.File;
-import java.io.FileReader;
import java.io.IOException;
public final class ShutdownThread extends Thread {
@@ -243,15 +243,7 @@
shutdownInner(context, confirm);
}
- private static void beginShutdownSequence(Context context) {
- synchronized (sIsStartedGuard) {
- if (sIsStarted) {
- Log.d(TAG, "Shutdown sequence already running, returning.");
- return;
- }
- sIsStarted = true;
- }
-
+ private static ProgressDialog showShutdownDialog(Context context) {
// Throw up a system dialog to indicate the device is rebooting / shutting down.
ProgressDialog pd = new ProgressDialog(context);
@@ -303,6 +295,32 @@
pd.setMessage(context.getText(
com.android.internal.R.string.reboot_to_reset_message));
pd.setIndeterminate(true);
+ } else if (mReason != null && mReason.equals(PowerManager.SHUTDOWN_USER_REQUESTED)) {
+ Dialog d = new Dialog(context);
+ d.setContentView(com.android.internal.R.layout.shutdown_dialog);
+ d.setCancelable(false);
+
+ int color = Color.WHITE;
+ try {
+ IOverlayManager service = IOverlayManager.Stub.asInterface(
+ ServiceManager.getService(Context.OVERLAY_SERVICE));
+ if (service.getOverlayInfo("com.android.systemui.theme.lightwallpaper", 0).isEnabled()) {
+ color = Color.BLACK;
+ }
+ } catch (Exception e) {
+ // Shutdown UI really shouldn't crash or have strict dependencies on other services.
+ Log.w(TAG, "Problem getting overlay state", e);
+ }
+ ProgressBar bar = d.findViewById(com.android.internal.R.id.progress);
+ bar.getIndeterminateDrawable().setTint(color);
+ ((TextView) d.findViewById(com.android.internal.R.id.text1)).setTextColor(color);
+ d.getWindow().getAttributes().width = ViewGroup.LayoutParams.MATCH_PARENT;
+ d.getWindow().getAttributes().height = ViewGroup.LayoutParams.MATCH_PARENT;
+ d.getWindow().setType(WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY);
+ d.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
+ d.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
+ d.show();
+ return null;
} else {
pd.setTitle(context.getText(com.android.internal.R.string.power_off));
pd.setMessage(context.getText(com.android.internal.R.string.shutdown_progress));
@@ -312,8 +330,19 @@
pd.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
pd.show();
+ return pd;
+ }
- sInstance.mProgressDialog = pd;
+ private static void beginShutdownSequence(Context context) {
+ synchronized (sIsStartedGuard) {
+ if (sIsStarted) {
+ Log.d(TAG, "Shutdown sequence already running, returning.");
+ return;
+ }
+ sIsStarted = true;
+ }
+
+ sInstance.mProgressDialog = showShutdownDialog(context);
sInstance.mContext = context;
sInstance.mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java
index 35e4e58c..8ce59ed 100644
--- a/services/core/java/com/android/server/trust/TrustManagerService.java
+++ b/services/core/java/com/android/server/trust/TrustManagerService.java
@@ -67,7 +67,6 @@
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.List;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -589,22 +588,20 @@
}
private void maybeEnableFactoryTrustAgents(LockPatternUtils utils, int userId) {
+ if (0 != Settings.Secure.getIntForUser(mContext.getContentResolver(),
+ Settings.Secure.TRUST_AGENTS_INITIALIZED, 0, userId)) {
+ return;
+ }
+ PackageManager pm = mContext.getPackageManager();
+ List<ResolveInfo> resolveInfos = resolveAllowedTrustAgents(pm, userId);
ComponentName defaultAgent = getDefaultFactoryTrustAgent(mContext);
boolean shouldUseDefaultAgent = defaultAgent != null;
+ ArraySet<ComponentName> discoveredAgents = new ArraySet<>();
if (shouldUseDefaultAgent) {
+ discoveredAgents.add(defaultAgent);
Log.i(TAG, "Enabling " + defaultAgent + " because it is a default agent.");
- utils.setEnabledTrustAgents(Collections.singleton(defaultAgent), userId);
} else { // A default agent is not set; perform regular trust agent discovery
- if (0 != Settings.Secure.getIntForUser(mContext.getContentResolver(),
- Settings.Secure.TRUST_AGENTS_INITIALIZED, 0, userId)) {
- return;
- }
- PackageManager pm = mContext.getPackageManager();
- List<ResolveInfo> resolveInfos = resolveAllowedTrustAgents(pm, userId);
-
- ArraySet<ComponentName> discoveredAgents = new ArraySet<>();
-
for (ResolveInfo resolveInfo : resolveInfos) {
ComponentName componentName = getComponentName(resolveInfo);
int applicationInfoFlags = resolveInfo.serviceInfo.applicationInfo.flags;
@@ -615,13 +612,13 @@
}
discoveredAgents.add(componentName);
}
-
- List<ComponentName> previouslyEnabledAgents = utils.getEnabledTrustAgents(userId);
- if (previouslyEnabledAgents != null) {
- discoveredAgents.addAll(previouslyEnabledAgents);
- }
- utils.setEnabledTrustAgents(discoveredAgents, userId);
}
+
+ List<ComponentName> previouslyEnabledAgents = utils.getEnabledTrustAgents(userId);
+ if (previouslyEnabledAgents != null) {
+ discoveredAgents.addAll(previouslyEnabledAgents);
+ }
+ utils.setEnabledTrustAgents(discoveredAgents, userId);
Settings.Secure.putIntForUser(mContext.getContentResolver(),
Settings.Secure.TRUST_AGENTS_INITIALIZED, 1, userId);
}
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index ad3ad508..ce44dab8 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -1343,15 +1343,38 @@
}
/**
+ * Returns whether the drawn window states of this {@link AppWindowToken} has considered every
+ * child {@link WindowState}. A child is considered if it has been passed into
+ * {@link #updateDrawnWindowStates(WindowState)} after being added. This is used to determine
+ * whether states, such as {@code allDrawn}, can be set, which relies on state variables such as
+ * {@code mNumInterestingWindows}, which depend on all {@link WindowState}s being considered.
+ *
+ * @return {@code true} If all children have been considered, {@code false}.
+ */
+ private boolean allDrawnStatesConsidered() {
+ for (WindowState child : mChildren) {
+ if (!child.getDrawnStatedEvaluated()) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
* 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
- // all windows to be launched and drawn for this token be considered all drawn
+ // all windows to be launched and drawn for this token be considered all drawn.
final int numInteresting = mNumInterestingWindows;
- if (numInteresting > 0 && mNumDrawnWindows >= numInteresting && !isRelaunching()) {
+
+ // We must make sure that all present children have been considered (determined by
+ // {@link #allDrawnStatesConsidered}) before evaluating whether everything has been
+ // drawn.
+ if (numInteresting > 0 && allDrawnStatesConsidered()
+ && mNumDrawnWindows >= numInteresting && !isRelaunching()) {
if (DEBUG_VISIBILITY) Slog.v(TAG, "allDrawn: " + this
+ " interesting=" + numInteresting + " drawn=" + mNumDrawnWindows);
allDrawn = true;
@@ -1396,6 +1419,8 @@
* windows in this app token where not considered drawn as of the last pass.
*/
boolean updateDrawnWindowStates(WindowState w) {
+ w.setDrawnStateEvaluated(true /*evaluated*/);
+
if (DEBUG_STARTING_WINDOW_VERBOSE && w == startingWindow) {
Slog.d(TAG, "updateWindows: starting " + w + " isOnScreen=" + w.isOnScreen()
+ " allDrawn=" + allDrawn + " freezingScreen=" + mAppAnimator.freezingScreen);
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 9fe7381..05f4626 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -1073,7 +1073,7 @@
}
if (w.mHasSurface && !rotateSeamlessly) {
if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Set mOrientationChanging of " + w);
- w.mOrientationChanging = true;
+ w.setOrientationChanging(true);
mService.mRoot.mOrientationChangeComplete = false;
w.mLastFreezeDuration = 0;
}
@@ -2679,10 +2679,10 @@
mService.mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_TIMEOUT;
forAllWindows(w -> {
- if (!w.mOrientationChanging) {
+ if (!w.getOrientationChanging()) {
return;
}
- w.mOrientationChanging = false;
+ w.setOrientationChanging(false);
w.mLastFreezeDuration = (int)(SystemClock.elapsedRealtime()
- mService.mDisplayFreezeTime);
Slog.w(TAG_WM, "Force clearing orientation change: " + w);
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java
index bf8fabd..b8b9b9a 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotController.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java
@@ -18,6 +18,9 @@
import static android.app.ActivityManager.ENABLE_TASK_SNAPSHOTS;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+
import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.ActivityManager.StackId;
@@ -29,6 +32,7 @@
import android.os.Environment;
import android.os.Handler;
import android.util.ArraySet;
+import android.util.Slog;
import android.view.DisplayListCanvas;
import android.view.RenderNode;
import android.view.ThreadedRenderer;
@@ -57,6 +61,7 @@
* To access this class, acquire the global window manager lock.
*/
class TaskSnapshotController {
+ private static final String TAG = TAG_WITH_CLASS_NAME ? "TaskSnapshotController" : TAG_WM;
/**
* Return value for {@link #getSnapshotMode}: We are allowed to take a real screenshot to be
@@ -147,10 +152,17 @@
break;
}
if (snapshot != null) {
- mCache.putSnapshot(task, snapshot);
- mPersister.persistSnapshot(task.mTaskId, task.mUserId, snapshot);
- if (task.getController() != null) {
- task.getController().reportSnapshotChanged(snapshot);
+ final GraphicBuffer buffer = snapshot.getSnapshot();
+ if (buffer.getWidth() == 0 || buffer.getHeight() == 0) {
+ buffer.destroy();
+ Slog.e(TAG, "Invalid task snapshot dimensions " + buffer.getWidth() + "x"
+ + buffer.getHeight());
+ } else {
+ mCache.putSnapshot(task, snapshot);
+ mPersister.persistSnapshot(task.mTaskId, task.mUserId, snapshot);
+ if (task.getController() != null) {
+ task.getController().reportSnapshotChanged(snapshot);
+ }
}
}
}
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotPersister.java b/services/core/java/com/android/server/wm/TaskSnapshotPersister.java
index b628869..f90b3fb 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotPersister.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotPersister.java
@@ -291,7 +291,6 @@
failed = true;
}
if (!writeBuffer()) {
- writeBuffer();
failed = true;
}
if (failed) {
@@ -327,11 +326,7 @@
final File reducedFile = getReducedResolutionBitmapFile(mTaskId, mUserId);
final Bitmap bitmap = Bitmap.createHardwareBitmap(mSnapshot.getSnapshot());
if (bitmap == null) {
- Slog.e(TAG, "Invalid task snapshot");
- return false;
- } else if (bitmap.getWidth() == 0 || bitmap.getHeight() == 0) {
- Slog.e(TAG, "Invalid task snapshot dimensions " + bitmap.getWidth() + "x"
- + bitmap.getHeight());
+ Slog.e(TAG, "Invalid task snapshot hw bitmap");
return false;
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index ebfeac3..1b4c34d 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -5789,7 +5789,7 @@
// orientation.
if (!okToDisplay() && mWindowsFreezingScreen != WINDOWS_FREEZING_SCREENS_TIMEOUT) {
if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Changing surface while display frozen: " + w);
- w.mOrientationChanging = true;
+ w.setOrientationChanging(true);
w.mLastFreezeDuration = 0;
mRoot.mOrientationChangeComplete = false;
if (mWindowsFreezingScreen == WINDOWS_FREEZING_SCREENS_NONE) {
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 5d32443..b403914 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -441,7 +441,7 @@
* Set when the orientation is changing and this window has not yet
* been updated for the new orientation.
*/
- boolean mOrientationChanging;
+ private boolean mOrientationChanging;
/**
* The orientation during the last visible call to relayout. If our
@@ -560,6 +560,13 @@
final Rect mLastSurfaceInsets = new Rect();
/**
+ * A flag set by the {@link WindowState} parent to indicate that the parent has examined this
+ * {@link WindowState} in its overall drawing context. This book-keeping allows the parent to
+ * make sure all children have been considered.
+ */
+ private boolean mDrawnStateEvaluated;
+
+ /**
* Compares two window sub-layers and returns -1 if the first is lesser than the second in terms
* of z-order and 1 otherwise.
*/
@@ -675,6 +682,27 @@
mSession.windowAddedLocked(mAttrs.packageName);
}
+ /**
+ * Returns whether this {@link WindowState} has been considered for drawing by its parent.
+ */
+ boolean getDrawnStatedEvaluated() {
+ return mDrawnStateEvaluated;
+ }
+
+ /**
+ * Sets whether this {@link WindowState} has been considered for drawing by its parent. Should
+ * be cleared when detached from parent.
+ */
+ void setDrawnStateEvaluated(boolean evaluated) {
+ mDrawnStateEvaluated = evaluated;
+ }
+
+ @Override
+ void onParentSet() {
+ super.onParentSet();
+ setDrawnStateEvaluated(false /*evaluated*/);
+ }
+
@Override
public int getOwningUid() {
return mOwnerUid;
@@ -1156,7 +1184,8 @@
// then we need to hold off on unfreezing the display until this window has been
// redrawn; to do that, we need to go through the process of getting informed by the
// application when it has finished drawing.
- if (mOrientationChanging || dragResizingChanged || isResizedWhileNotDragResizing()) {
+ if (getOrientationChanging() || dragResizingChanged
+ || isResizedWhileNotDragResizing()) {
if (DEBUG_SURFACE_TRACE || DEBUG_ANIM || DEBUG_ORIENTATION || DEBUG_RESIZE) {
Slog.v(TAG_WM, "Orientation or resize start waiting for draw"
+ ", mDrawState=DRAW_PENDING in " + this
@@ -1171,17 +1200,33 @@
if (DEBUG_RESIZE || DEBUG_ORIENTATION) Slog.v(TAG_WM, "Resizing window " + this);
mService.mResizingWindows.add(this);
}
- } else if (mOrientationChanging) {
+ } else if (getOrientationChanging()) {
if (isDrawnLw()) {
if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Orientation not waiting for draw in "
+ this + ", surfaceController " + winAnimator.mSurfaceController);
- mOrientationChanging = false;
+ setOrientationChanging(false);
mLastFreezeDuration = (int)(SystemClock.elapsedRealtime()
- mService.mDisplayFreezeTime);
}
}
}
+ boolean getOrientationChanging() {
+ // In addition to the local state flag, we must also consider the difference in the last
+ // reported configuration vs. the current state. If the client code has not been informed of
+ // the change, logic dependent on having finished processing the orientation, such as
+ // unfreezing, could be improperly triggered.
+ // TODO(b/62846907): Checking against {@link mLastReportedConfiguration} could be flaky as
+ // this is not necessarily what the client has processed yet. Find a
+ // better indicator consistent with the client.
+ return mOrientationChanging
+ || getConfiguration().orientation != mLastReportedConfiguration.orientation;
+ }
+
+ void setOrientationChanging(boolean changing) {
+ mOrientationChanging = changing;
+ }
+
DisplayContent getDisplayContent() {
return mToken.getDisplayContent();
}
@@ -2635,10 +2680,10 @@
mAppFreezing = false;
- if (mHasSurface && !mOrientationChanging
+ if (mHasSurface && !getOrientationChanging()
&& mService.mWindowsFreezingScreen != WINDOWS_FREEZING_SCREENS_TIMEOUT) {
if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "set mOrientationChanging of " + this);
- mOrientationChanging = true;
+ setOrientationChanging(true);
mService.mRoot.mOrientationChangeComplete = false;
}
mLastFreezeDuration = 0;
@@ -3057,7 +3102,7 @@
mWinAnimator.mSurfaceResized = false;
mReportOrientationChanged = false;
} catch (RemoteException e) {
- mOrientationChanging = false;
+ setOrientationChanging(false);
mLastFreezeDuration = (int)(SystemClock.elapsedRealtime()
- mService.mDisplayFreezeTime);
// We are assuming the hosting process is dead or in a zombie state.
@@ -3416,10 +3461,13 @@
pw.print(" mDestroying="); pw.print(mDestroying);
pw.print(" mRemoved="); pw.println(mRemoved);
}
- if (mOrientationChanging || mAppFreezing || mTurnOnScreen
+ if (getOrientationChanging() || mAppFreezing || mTurnOnScreen
|| mReportOrientationChanged) {
pw.print(prefix); pw.print("mOrientationChanging=");
pw.print(mOrientationChanging);
+ pw.print(" configOrientationChanging=");
+ pw.print(mLastReportedConfiguration.orientation
+ != getConfiguration().orientation);
pw.print(" mAppFreezing="); pw.print(mAppFreezing);
pw.print(" mTurnOnScreen="); pw.print(mTurnOnScreen);
pw.print(" mReportOrientationChanged="); pw.println(mReportOrientationChanged);
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index cd55156..8f1065f 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -1527,11 +1527,11 @@
// There is no need to wait for an animation change if our window is gone for layout
// already as we'll never be visible.
- if (w.mOrientationChanging && w.isGoneForLayoutLw()) {
+ if (w.getOrientationChanging() && w.isGoneForLayoutLw()) {
if (DEBUG_ORIENTATION) {
Slog.v(TAG, "Orientation change skips hidden " + w);
}
- w.mOrientationChanging = false;
+ w.setOrientationChanging(false);
}
return;
}
@@ -1564,8 +1564,8 @@
// really hidden (gone for layout), there is no point in still waiting for it.
// Note that this does introduce a potential glitch if the window becomes unhidden
// before it has drawn for the new orientation.
- if (w.mOrientationChanging && w.isGoneForLayoutLw()) {
- w.mOrientationChanging = false;
+ if (w.getOrientationChanging() && w.isGoneForLayoutLw()) {
+ w.setOrientationChanging(false);
if (DEBUG_ORIENTATION) Slog.v(TAG,
"Orientation change skips hidden " + w);
}
@@ -1618,7 +1618,7 @@
mAnimator.setPendingLayoutChanges(w.getDisplayId(),
WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM);
} else {
- w.mOrientationChanging = false;
+ w.setOrientationChanging(false);
}
}
if (hasSurface()) {
@@ -1631,14 +1631,14 @@
displayed = true;
}
- if (w.mOrientationChanging) {
+ if (w.getOrientationChanging()) {
if (!w.isDrawnLw()) {
mAnimator.mBulkUpdateParams &= ~SET_ORIENTATION_CHANGE_COMPLETE;
mAnimator.mLastWindowFreezeSource = w;
if (DEBUG_ORIENTATION) Slog.v(TAG,
"Orientation continue waiting for draw in " + w);
} else {
- w.mOrientationChanging = false;
+ w.setOrientationChanging(false);
if (DEBUG_ORIENTATION) Slog.v(TAG, "Orientation change complete in " + w);
}
}
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index 42272fd..b0fefc4 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -693,8 +693,9 @@
// Set the new USB configuration.
setUsbConfig(oemFunctions);
- if (UsbManager.containsFunction(functions, UsbManager.USB_FUNCTION_MTP)
- || UsbManager.containsFunction(functions, UsbManager.USB_FUNCTION_PTP)) {
+ if (mBootCompleted
+ && (UsbManager.containsFunction(functions, UsbManager.USB_FUNCTION_MTP)
+ || UsbManager.containsFunction(functions, UsbManager.USB_FUNCTION_PTP))) {
// Start up dependent services.
updateUsbStateBroadcastIfNeeded(true);
}
diff --git a/telecomm/java/android/telecom/DisconnectCause.java b/telecomm/java/android/telecom/DisconnectCause.java
index e6b567e..dcf5c27 100644
--- a/telecomm/java/android/telecom/DisconnectCause.java
+++ b/telecomm/java/android/telecom/DisconnectCause.java
@@ -83,6 +83,13 @@
*/
public static final String REASON_WIFI_ON_BUT_WFC_OFF = "REASON_WIFI_ON_BUT_WFC_OFF";
+ /**
+ * Reason code (returned via {@link #getReason()}), which indicates that the video telephony
+ * call was disconnected because IMS access is blocked.
+ * @hide
+ */
+ public static final String REASON_IMS_ACCESS_BLOCKED = "REASON_IMS_ACCESS_BLOCKED";
+
private int mDisconnectCode;
private CharSequence mDisconnectLabel;
private CharSequence mDisconnectDescription;
diff --git a/tests/net/java/com/android/server/connectivity/TetheringTest.java b/tests/net/java/com/android/server/connectivity/TetheringTest.java
index ab874ce..acf9e8f7 100644
--- a/tests/net/java/com/android/server/connectivity/TetheringTest.java
+++ b/tests/net/java/com/android/server/connectivity/TetheringTest.java
@@ -16,6 +16,11 @@
package com.android.server.connectivity;
+import static android.hardware.usb.UsbManager.USB_CONFIGURED;
+import static android.hardware.usb.UsbManager.USB_CONNECTED;
+import static android.hardware.usb.UsbManager.USB_FUNCTION_RNDIS;
+import static android.net.ConnectivityManager.TETHERING_WIFI;
+import static android.net.ConnectivityManager.TETHERING_USB;
import static android.net.wifi.WifiManager.IFACE_IP_MODE_LOCAL_ONLY;
import static android.net.wifi.WifiManager.IFACE_IP_MODE_TETHERED;
import static android.net.wifi.WifiManager.EXTRA_WIFI_AP_INTERFACE_NAME;
@@ -133,6 +138,7 @@
public Object getSystemService(String name) {
if (Context.CONNECTIVITY_SERVICE.equals(name)) return mConnectivityManager;
if (Context.WIFI_SERVICE.equals(name)) return mWifiManager;
+ if (Context.USB_SERVICE.equals(name)) return mUsbManager;
return super.getSystemService(name);
}
}
@@ -145,7 +151,7 @@
when(mResources.getStringArray(com.android.internal.R.array.config_tether_usb_regexs))
.thenReturn(new String[0]);
when(mResources.getStringArray(com.android.internal.R.array.config_tether_wifi_regexs))
- .thenReturn(new String[]{ "test_wlan\\d" });
+ .thenReturn(new String[]{ "test_wlan\\d", "test_rndis\\d" });
when(mResources.getStringArray(com.android.internal.R.array.config_tether_bluetooth_regexs))
.thenReturn(new String[0]);
when(mResources.getIntArray(com.android.internal.R.array.config_tether_upstream_types))
@@ -245,6 +251,14 @@
mServiceContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
}
+ private void sendUsbBroadcast(boolean connected, boolean configured, boolean rndisFunction) {
+ final Intent intent = new Intent(UsbManager.ACTION_USB_STATE);
+ intent.putExtra(USB_CONNECTED, connected);
+ intent.putExtra(USB_CONFIGURED, configured);
+ intent.putExtra(USB_FUNCTION_RNDIS, rndisFunction);
+ mServiceContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
+ }
+
private void verifyInterfaceServingModeStarted() throws Exception {
verify(mNMService, times(1)).getInterfaceConfig(mTestIfname);
verify(mNMService, times(1))
@@ -287,6 +301,32 @@
}
@Test
+ public void testUsbConfiguredBroadcastStartsTethering() throws Exception {
+ when(mConnectivityManager.isTetheringSupported()).thenReturn(true);
+
+ // Emulate pressing the USB tethering button in Settings UI.
+ mTethering.startTethering(TETHERING_USB, null, false);
+ mLooper.dispatchAll();
+ verify(mUsbManager, times(1)).setCurrentFunction(UsbManager.USB_FUNCTION_RNDIS, false);
+
+ // Pretend we receive a USB connected broadcast. Here we also pretend
+ // that the RNDIS function is somehow enabled, so that we see if we
+ // might trip ourselves up.
+ sendUsbBroadcast(true, false, true);
+ mLooper.dispatchAll();
+ // This should produce no activity of any kind.
+ verifyNoMoreInteractions(mConnectivityManager);
+ verifyNoMoreInteractions(mNMService);
+
+ // Pretend we then receive USB configured broadcast.
+ sendUsbBroadcast(true, true, true);
+ mLooper.dispatchAll();
+ // Now we should see the start of tethering mechanics (in this case:
+ // tetherMatchingInterfaces() which starts by fetching all interfaces).
+ verify(mNMService, times(1)).listInterfaces();
+ }
+
+ @Test
public void failingLocalOnlyHotspotLegacyApBroadcastWithIfaceStatusChanged() throws Exception {
failingLocalOnlyHotspotLegacyApBroadcast(true);
}
@@ -366,7 +406,7 @@
when(mWifiManager.startSoftAp(any(WifiConfiguration.class))).thenReturn(true);
// Emulate pressing the WiFi tethering button.
- mTethering.startTethering(ConnectivityManager.TETHERING_WIFI, null, false);
+ mTethering.startTethering(TETHERING_WIFI, null, false);
mLooper.dispatchAll();
verify(mWifiManager, times(1)).startSoftAp(null);
verifyNoMoreInteractions(mWifiManager);
@@ -394,7 +434,7 @@
when(mWifiManager.startSoftAp(any(WifiConfiguration.class))).thenReturn(true);
// Emulate pressing the WiFi tethering button.
- mTethering.startTethering(ConnectivityManager.TETHERING_WIFI, null, false);
+ mTethering.startTethering(TETHERING_WIFI, null, false);
mLooper.dispatchAll();
verify(mWifiManager, times(1)).startSoftAp(null);
verifyNoMoreInteractions(mWifiManager);
@@ -440,7 +480,7 @@
/////
// Emulate pressing the WiFi tethering button.
- mTethering.stopTethering(ConnectivityManager.TETHERING_WIFI);
+ mTethering.stopTethering(TETHERING_WIFI);
mLooper.dispatchAll();
verify(mWifiManager, times(1)).stopSoftAp();
verifyNoMoreInteractions(mWifiManager);
@@ -476,7 +516,7 @@
doThrow(new RemoteException()).when(mNMService).setIpForwardingEnabled(true);
// Emulate pressing the WiFi tethering button.
- mTethering.startTethering(ConnectivityManager.TETHERING_WIFI, null, false);
+ mTethering.startTethering(TETHERING_WIFI, null, false);
mLooper.dispatchAll();
verify(mWifiManager, times(1)).startSoftAp(null);
verifyNoMoreInteractions(mWifiManager);