Merge "Add 'more settings' and 'done' to guts."
diff --git a/api/current.txt b/api/current.txt
index 3854b7a..76e44cf 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -4082,11 +4082,14 @@
}
public class DatePickerDialog extends android.app.AlertDialog implements android.widget.DatePicker.OnDateChangedListener android.content.DialogInterface.OnClickListener {
+ ctor public DatePickerDialog(android.content.Context);
+ ctor public DatePickerDialog(android.content.Context, int);
ctor public DatePickerDialog(android.content.Context, android.app.DatePickerDialog.OnDateSetListener, int, int, int);
ctor public DatePickerDialog(android.content.Context, int, android.app.DatePickerDialog.OnDateSetListener, int, int, int);
method public android.widget.DatePicker getDatePicker();
method public void onClick(android.content.DialogInterface, int);
method public void onDateChanged(android.widget.DatePicker, int, int, int);
+ method public void setOnDateSetListener(android.app.DatePickerDialog.OnDateSetListener);
method public void updateDate(int, int, int);
}
diff --git a/api/system-current.txt b/api/system-current.txt
index 2990516..27a16bf 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -4202,11 +4202,14 @@
}
public class DatePickerDialog extends android.app.AlertDialog implements android.widget.DatePicker.OnDateChangedListener android.content.DialogInterface.OnClickListener {
+ ctor public DatePickerDialog(android.content.Context);
+ ctor public DatePickerDialog(android.content.Context, int);
ctor public DatePickerDialog(android.content.Context, android.app.DatePickerDialog.OnDateSetListener, int, int, int);
ctor public DatePickerDialog(android.content.Context, int, android.app.DatePickerDialog.OnDateSetListener, int, int, int);
method public android.widget.DatePicker getDatePicker();
method public void onClick(android.content.DialogInterface, int);
method public void onDateChanged(android.widget.DatePicker, int, int, int);
+ method public void setOnDateSetListener(android.app.DatePickerDialog.OnDateSetListener);
method public void updateDate(int, int, int);
}
diff --git a/api/test-current.txt b/api/test-current.txt
index 83d12fc..cee8fad 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -4082,11 +4082,14 @@
}
public class DatePickerDialog extends android.app.AlertDialog implements android.widget.DatePicker.OnDateChangedListener android.content.DialogInterface.OnClickListener {
+ ctor public DatePickerDialog(android.content.Context);
+ ctor public DatePickerDialog(android.content.Context, int);
ctor public DatePickerDialog(android.content.Context, android.app.DatePickerDialog.OnDateSetListener, int, int, int);
ctor public DatePickerDialog(android.content.Context, int, android.app.DatePickerDialog.OnDateSetListener, int, int, int);
method public android.widget.DatePicker getDatePicker();
method public void onClick(android.content.DialogInterface, int);
method public void onDateChanged(android.widget.DatePicker, int, int, int);
+ method public void setOnDateSetListener(android.app.DatePickerDialog.OnDateSetListener);
method public void updateDate(int, int, int);
}
diff --git a/core/java/android/app/DatePickerDialog.java b/core/java/android/app/DatePickerDialog.java
index 3fbbdff..8d7f347 100644
--- a/core/java/android/app/DatePickerDialog.java
+++ b/core/java/android/app/DatePickerDialog.java
@@ -16,6 +16,9 @@
package android.app;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.StyleRes;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
@@ -35,95 +38,126 @@
/**
* A simple dialog containing an {@link android.widget.DatePicker}.
- *
- * <p>See the <a href="{@docRoot}guide/topics/ui/controls/pickers.html">Pickers</a>
- * guide.</p>
+ * <p>
+ * See the <a href="{@docRoot}guide/topics/ui/controls/pickers.html">Pickers</a>
+ * guide.
*/
public class DatePickerDialog extends AlertDialog implements OnClickListener,
OnDateChangedListener {
-
private static final String YEAR = "year";
private static final String MONTH = "month";
private static final String DAY = "day";
private final DatePicker mDatePicker;
- private final OnDateSetListener mDateSetListener;
private final Calendar mCalendar;
+ private OnDateSetListener mDateSetListener;
+
private boolean mTitleNeedsUpdate = true;
/**
- * The callback used to indicate the user is done filling in the date.
+ * Creates a new date picker dialog for the current date using the parent
+ * context's default date picker dialog theme.
+ *
+ * @param context the parent context
*/
- public interface OnDateSetListener {
-
- /**
- * @param view The view associated with this listener.
- * @param year The year that was set.
- * @param monthOfYear The month that was set (0-11) for compatibility
- * with {@link java.util.Calendar}.
- * @param dayOfMonth The day of the month that was set.
- */
- void onDateSet(DatePicker view, int year, int monthOfYear, int dayOfMonth);
+ public DatePickerDialog(Context context) {
+ this(context, 0);
}
/**
- * @param context The context the dialog is to run in.
- * @param callBack How the parent is notified that the date is set.
- * @param year The initial year of the dialog.
- * @param monthOfYear The initial month of the dialog.
- * @param dayOfMonth The initial day of the dialog.
+ * Creates a new date picker dialog for the current date.
+ *
+ * @param context the parent context
+ * @param themeResId the resource ID of the theme against which to inflate
+ * this dialog, or {@code 0} to use the parent
+ * {@code context}'s default alert dialog theme
*/
- public DatePickerDialog(Context context,
- OnDateSetListener callBack,
- int year,
- int monthOfYear,
- int dayOfMonth) {
- this(context, 0, callBack, year, monthOfYear, dayOfMonth);
- }
-
- static int resolveDialogTheme(Context context, int resid) {
- if (resid == 0) {
- final TypedValue outValue = new TypedValue();
- context.getTheme().resolveAttribute(R.attr.datePickerDialogTheme, outValue, true);
- return outValue.resourceId;
- } else {
- return resid;
- }
- }
-
- /**
- * @param context The context the dialog is to run in.
- * @param theme the theme to apply to this dialog
- * @param listener How the parent is notified that the date is set.
- * @param year The initial year of the dialog.
- * @param monthOfYear The initial month of the dialog.
- * @param dayOfMonth The initial day of the dialog.
- */
- public DatePickerDialog(Context context, int theme, OnDateSetListener listener, int year,
- int monthOfYear, int dayOfMonth) {
- super(context, resolveDialogTheme(context, theme));
-
- mDateSetListener = listener;
- mCalendar = Calendar.getInstance();
+ public DatePickerDialog(Context context, @StyleRes int themeResId) {
+ super(context, resolveDialogTheme(context, themeResId));
final Context themeContext = getContext();
final LayoutInflater inflater = LayoutInflater.from(themeContext);
final View view = inflater.inflate(R.layout.date_picker_dialog, null);
setView(view);
+
setButton(BUTTON_POSITIVE, themeContext.getString(R.string.ok), this);
setButton(BUTTON_NEGATIVE, themeContext.getString(R.string.cancel), this);
setButtonPanelLayoutHint(LAYOUT_HINT_SIDE);
+ mCalendar = Calendar.getInstance();
+
+ final int year = mCalendar.get(Calendar.YEAR);
+ final int monthOfYear = mCalendar.get(Calendar.MONTH);
+ final int dayOfMonth = mCalendar.get(Calendar.DAY_OF_MONTH);
mDatePicker = (DatePicker) view.findViewById(R.id.datePicker);
mDatePicker.init(year, monthOfYear, dayOfMonth, this);
mDatePicker.setValidationCallback(mValidationCallback);
}
+ /**
+ * Creates a new date picker dialog for the specified date using the parent
+ * context's default date picker dialog theme.
+ *
+ * @param context the parent context
+ * @param listener the listener to call when the user sets the date
+ * @param year the initially selected year
+ * @param month the initially selected month (0-11 for compatibility with
+ * {@link Calendar#MONTH})
+ * @param dayOfMonth the initially selected day of month (1-31, depending
+ * on month)
+ */
+ public DatePickerDialog(@Nullable Context context, @Nullable OnDateSetListener listener,
+ int year, int month, int dayOfMonth) {
+ this(context, 0, listener, year, month, dayOfMonth);
+ }
+
+ /**
+ * Creates a new date picker dialog for the specified date.
+ *
+ * @param context the parent context
+ * @param themeResId the resource ID of the theme against which to inflate
+ * this dialog, or {@code 0} to use the parent
+ * {@code context}'s default alert dialog theme
+ * @param listener the listener to call when the user sets the date
+ * @param year the initially selected year
+ * @param month the initially selected month (0-11 for compatibility with
+ * {@link Calendar#MONTH})
+ * @param dayOfMonth the initially selected day of month (1-31, depending
+ * on month)
+ */
+ public DatePickerDialog(@NonNull Context context, @StyleRes int themeResId,
+ @Nullable OnDateSetListener listener, int year, int month, int dayOfMonth) {
+ this(context, themeResId);
+
+ mDateSetListener = listener;
+
+ mDatePicker.updateDate(year, month, dayOfMonth);
+ }
+
+ static int resolveDialogTheme(@NonNull Context context, @StyleRes int themeResId) {
+ if (themeResId == 0) {
+ final TypedValue outValue = new TypedValue();
+ context.getTheme().resolveAttribute(R.attr.datePickerDialogTheme, outValue, true);
+ return outValue.resourceId;
+ } else {
+ return themeResId;
+ }
+ }
+
@Override
- public void onDateChanged(DatePicker view, int year, int month, int day) {
- mDatePicker.init(year, month, day, this);
- updateTitle(year, month, day);
+ public void onDateChanged(DatePicker view, int year, int month, int dayOfMonth) {
+ mDatePicker.init(year, month, dayOfMonth, this);
+ updateTitle(year, month, dayOfMonth);
+ }
+
+ /**
+ * Sets the listener to call when the user sets the date.
+ *
+ * @param listener the listener to call when the user sets the date
+ */
+ public void setOnDateSetListener(@Nullable OnDateSetListener listener) {
+ mDateSetListener = listener;
}
@Override
@@ -145,10 +179,11 @@
}
/**
- * Gets the {@link DatePicker} contained in this dialog.
+ * Returns the {@link DatePicker} contained in this dialog.
*
- * @return The calendar view.
+ * @return the date picker
*/
+ @NonNull
public DatePicker getDatePicker() {
return mDatePicker;
}
@@ -156,20 +191,22 @@
/**
* Sets the current date.
*
- * @param year The date year.
- * @param monthOfYear The date month.
- * @param dayOfMonth The date day of month.
+ * @param year the year
+ * @param month the month (0-11 for compatibility with
+ * {@link Calendar#MONTH})
+ * @param dayOfMonth the day of month (1-31, depending on month)
*/
- public void updateDate(int year, int monthOfYear, int dayOfMonth) {
- mDatePicker.updateDate(year, monthOfYear, dayOfMonth);
+ public void updateDate(int year, int month, int dayOfMonth) {
+ mDatePicker.updateDate(year, month, dayOfMonth);
}
- private void updateTitle(int year, int month, int day) {
+ private void updateTitle(int year, int month, int dayOfMonth) {
if (!mDatePicker.getCalendarViewShown()) {
mCalendar.set(Calendar.YEAR, year);
mCalendar.set(Calendar.MONTH, month);
- mCalendar.set(Calendar.DAY_OF_MONTH, day);
- String title = DateUtils.formatDateTime(mContext,
+ mCalendar.set(Calendar.DAY_OF_MONTH, dayOfMonth);
+
+ final String title = DateUtils.formatDateTime(mContext,
mCalendar.getTimeInMillis(),
DateUtils.FORMAT_SHOW_DATE
| DateUtils.FORMAT_SHOW_WEEKDAY
@@ -177,12 +214,12 @@
| DateUtils.FORMAT_ABBREV_MONTH
| DateUtils.FORMAT_ABBREV_WEEKDAY);
setTitle(title);
+
mTitleNeedsUpdate = true;
- } else {
- if (mTitleNeedsUpdate) {
- mTitleNeedsUpdate = false;
- setTitle(R.string.date_picker_dialog_title);
- }
+ } else if (mTitleNeedsUpdate) {
+ setTitle(R.string.date_picker_dialog_title);
+
+ mTitleNeedsUpdate = false;
}
}
@@ -213,4 +250,19 @@
}
}
};
+
+ /**
+ * The listener used to indicate the user has finished selecting a date.
+ */
+ public interface OnDateSetListener {
+ /**
+ * @param view the picker associated with the dialog
+ * @param year the selected year
+ * @param month the selected month (0-11 for compatibility with
+ * {@link Calendar#MONTH})
+ * @param dayOfMonth th selected day of the month (1-31, depending on
+ * month)
+ */
+ void onDateSet(DatePicker view, int year, int month, int dayOfMonth);
+ }
}
diff --git a/core/java/android/security/net/config/ApplicationConfig.java b/core/java/android/security/net/config/ApplicationConfig.java
index 71d9d5d..4de36cd 100644
--- a/core/java/android/security/net/config/ApplicationConfig.java
+++ b/core/java/android/security/net/config/ApplicationConfig.java
@@ -120,6 +120,32 @@
return mTrustManager;
}
+ /**
+ * Returns {@code true} if cleartext traffic is permitted for this application, which is the
+ * case only if all configurations permit cleartext traffic. For finer-grained policy use
+ * {@link #isCleartextTrafficPermitted(String)}.
+ */
+ public boolean isCleartextTrafficPermitted() {
+ ensureInitialized();
+ if (mConfigs != null) {
+ for (Pair<Domain, NetworkSecurityConfig> entry : mConfigs) {
+ if (!entry.second.isCleartextTrafficPermitted()) {
+ return false;
+ }
+ }
+ }
+
+ return mDefaultConfig.isCleartextTrafficPermitted();
+ }
+
+ /**
+ * Returns {@code true} if cleartext traffic is permitted for this application when connecting
+ * to {@code hostname}.
+ */
+ public boolean isCleartextTrafficPermitted(String hostname) {
+ return getConfigForHostname(hostname).isCleartextTrafficPermitted();
+ }
+
private void ensureInitialized() {
synchronized(mLock) {
if (mInitialized) {
diff --git a/core/java/android/security/net/config/ConfigNetworkSecurityPolicy.java b/core/java/android/security/net/config/ConfigNetworkSecurityPolicy.java
new file mode 100644
index 0000000..e7d17c2
--- /dev/null
+++ b/core/java/android/security/net/config/ConfigNetworkSecurityPolicy.java
@@ -0,0 +1,40 @@
+/**
+ * Copyright (c) 2015, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.net.config;
+
+/**
+ * {@link libcore.net.NetworkSecurityPolicy} based on an {@link ApplicationConfig}.
+ *
+ * @hide
+ */
+public class ConfigNetworkSecurityPolicy extends libcore.net.NetworkSecurityPolicy {
+ private final ApplicationConfig mConfig;
+
+ public ConfigNetworkSecurityPolicy(ApplicationConfig config) {
+ mConfig = config;
+ }
+
+ @Override
+ public boolean isCleartextTrafficPermitted() {
+ return mConfig.isCleartextTrafficPermitted();
+ }
+
+ @Override
+ public boolean isCleartextTrafficPermitted(String hostname) {
+ return mConfig.isCleartextTrafficPermitted(hostname);
+ }
+}
diff --git a/core/java/android/security/net/config/NetworkSecurityConfigProvider.java b/core/java/android/security/net/config/NetworkSecurityConfigProvider.java
index 5ebc7ac..0f66873 100644
--- a/core/java/android/security/net/config/NetworkSecurityConfigProvider.java
+++ b/core/java/android/security/net/config/NetworkSecurityConfigProvider.java
@@ -40,5 +40,6 @@
throw new RuntimeException("Failed to install provider as highest priority provider."
+ " Provider was installed at position " + pos);
}
+ libcore.net.NetworkSecurityPolicy.setInstance(new ConfigNetworkSecurityPolicy(config));
}
}
diff --git a/core/java/android/service/quicksettings/TileService.java b/core/java/android/service/quicksettings/TileService.java
index d8787b4..55fe4cd 100644
--- a/core/java/android/service/quicksettings/TileService.java
+++ b/core/java/android/service/quicksettings/TileService.java
@@ -26,17 +26,17 @@
import android.view.WindowManager;
/**
- * A QSTileService provides the user a tile that can be added to Quick Settings.
+ * A TileService provides the user a tile that can be added to Quick Settings.
* Quick Settings is a space provided that allows the user to change settings and
* take quick actions without leaving the context of their current app.
*
- * <p>The lifecycle of a QSTileService is different from some other services in
+ * <p>The lifecycle of a TileService is different from some other services in
* that it may be unbound during parts of its lifecycle. Any of the following
* lifecycle events can happen indepently in a separate binding/creation of the
* service.</p>
*
* <ul>
- * <li>When a tile is added by the user its QSTileService will be bound to and
+ * <li>When a tile is added by the user its TileService will be bound to and
* {@link #onTileAdded()} will be called.</li>
*
* <li>When a tile should be up to date and listing will be indicated by
@@ -45,10 +45,10 @@
* <li>When the user removes a tile from Quick Settings {@link #onStopListening()}
* will be called.</li>
* </ul>
- * <p>QSTileService will be detected by tiles that match the {@value #ACTION_QS_TILE}
+ * <p>TileService will be detected by tiles that match the {@value #ACTION_QS_TILE}
* and require the permission "android.permission.BIND_QUICK_SETTINGS_TILE".
* The label and icon for the service will be used as the default label and
- * icon for the tile. Here is an example QSTileService declaration.</p>
+ * icon for the tile. Here is an example TileService declaration.</p>
* <pre class="prettyprint">
* {@literal
* <service
@@ -67,7 +67,7 @@
public class TileService extends Service {
/**
- * Action that identifies a Service as being a QSTileService.
+ * Action that identifies a Service as being a TileService.
*/
public static final String ACTION_QS_TILE = "android.service.quicksettings.action.QS_TILE";
diff --git a/core/java/android/view/LayoutInflater.java b/core/java/android/view/LayoutInflater.java
index aa29636..914bd56 100644
--- a/core/java/android/view/LayoutInflater.java
+++ b/core/java/android/view/LayoutInflater.java
@@ -555,6 +555,26 @@
}
}
+ private static final ClassLoader BOOT_CLASS_LOADER = LayoutInflater.class.getClassLoader();
+
+ private final boolean verifyClassLoader(Constructor<? extends View> constructor) {
+ final ClassLoader constructorLoader = constructor.getDeclaringClass().getClassLoader();
+ if (constructorLoader == BOOT_CLASS_LOADER) {
+ // fast path for boot class loader (most common case?) - always ok
+ return true;
+ }
+ // in all normal cases (no dynamic code loading), we will exit the following loop on the
+ // first iteration (i.e. when the declaring classloader is the contexts class loader).
+ ClassLoader cl = mContext.getClassLoader();
+ do {
+ if (constructorLoader == cl) {
+ return true;
+ }
+ cl = cl.getParent();
+ } while (cl != null);
+ return false;
+ }
+
/**
* Low-level function for instantiating a view by name. This attempts to
* instantiate a view class of the given <var>name</var> found in this
@@ -575,6 +595,10 @@
public final View createView(String name, String prefix, AttributeSet attrs)
throws ClassNotFoundException, InflateException {
Constructor<? extends View> constructor = sConstructorMap.get(name);
+ if (constructor != null && !verifyClassLoader(constructor)) {
+ constructor = null;
+ sConstructorMap.remove(name);
+ }
Class<? extends View> clazz = null;
try {
diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java
index 19e290b..7e98193 100644
--- a/core/java/android/widget/PopupWindow.java
+++ b/core/java/android/widget/PopupWindow.java
@@ -1467,10 +1467,14 @@
}
if (mClipToScreen) {
+ final int winOffsetX = mScreenLocation[0] - mDrawingLocation[0];
+ final int winOffsetY = mScreenLocation[1] - mDrawingLocation[1];
+ p.x += winOffsetX;
+ p.y += winOffsetY;
final int displayFrameWidth = displayFrame.right - displayFrame.left;
final int right = p.x + p.width;
- if (right > displayFrameWidth) {
- p.x -= right - displayFrameWidth;
+ if (right > displayFrame.right) {
+ p.x -= right - displayFrame.right;
}
if (p.x < displayFrame.left) {
@@ -1479,10 +1483,9 @@
}
if (mOverlapAnchor) {
- final int displayFrameHeight = displayFrame.bottom - displayFrame.top;
final int bottom = p.y + p.height;
if (bottom > displayFrame.bottom) {
- p.y -= bottom - displayFrameHeight;
+ p.y -= bottom - displayFrame.bottom;
}
} else {
if (onTop) {
@@ -1494,6 +1497,8 @@
p.y = Math.max(p.y, displayFrame.top);
}
}
+ p.x -= winOffsetX;
+ p.y -= winOffsetY;
}
p.gravity |= Gravity.DISPLAY_CLIP_VERTICAL;
diff --git a/core/java/com/android/internal/logging/MetricsLogger.java b/core/java/com/android/internal/logging/MetricsLogger.java
index bfc56db..5fc7448 100644
--- a/core/java/com/android/internal/logging/MetricsLogger.java
+++ b/core/java/com/android/internal/logging/MetricsLogger.java
@@ -36,6 +36,7 @@
public static final int NOTIFICATION_TOPIC_NOTIFICATION = 263;
public static final int ACTION_DEFAULT_SMS_APP_CHANGED = 264;
public static final int QS_COLOR_MATRIX = 265;
+ public static final int QS_CUSTOM = 266;
/**
* Logged when the user docks a window from recents by longpressing a task and dragging it to
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 1090f90..01edb8e 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1901,7 +1901,7 @@
<!-- Allows an application to bind to third party quick settings tiles.
<p>Should only be requested by the System, should be required by
- QSTileService declarations.-->
+ TileService declarations.-->
<permission android:name="android.permission.BIND_QUICK_SETTINGS_TILE"
android:protectionLevel="signature" />
diff --git a/core/res/res/layout/text_drag_thumbnail.xml b/core/res/res/layout/text_drag_thumbnail.xml
index 63d2c05..b1a58cd 100644
--- a/core/res/res/layout/text_drag_thumbnail.xml
+++ b/core/res/res/layout/text_drag_thumbnail.xml
@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
-/* apps/common/assets/default/default/skins/StatusBar.xml
**
** Copyright 2010, The Android Open Source Project
**
diff --git a/libs/hwui/JankTracker.cpp b/libs/hwui/JankTracker.cpp
index eb9b55f..c305f65 100644
--- a/libs/hwui/JankTracker.cpp
+++ b/libs/hwui/JankTracker.cpp
@@ -243,6 +243,7 @@
dprintf(fd, "\nTotal frames rendered: %u", data->totalFrameCount);
dprintf(fd, "\nJanky frames: %u (%.2f%%)", data->jankFrameCount,
(float) data->jankFrameCount / (float) data->totalFrameCount * 100.0f);
+ dprintf(fd, "\n50th percentile: %ums", findPercentile(data, 50));
dprintf(fd, "\n90th percentile: %ums", findPercentile(data, 90));
dprintf(fd, "\n95th percentile: %ums", findPercentile(data, 95));
dprintf(fd, "\n99th percentile: %ums", findPercentile(data, 99));
diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp
index 0669596..083aeb7 100644
--- a/libs/hwui/Properties.cpp
+++ b/libs/hwui/Properties.cpp
@@ -53,6 +53,8 @@
ProfileType Properties::sProfileType = ProfileType::None;
bool Properties::sDisableProfileBars = false;
+bool Properties::waitForGpuCompletion = false;
+
static int property_get_int(const char* key, int defaultValue) {
char buf[PROPERTY_VALUE_MAX] = {'\0',};
diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h
index 1dde7e0..88f1dbc 100644
--- a/libs/hwui/Properties.h
+++ b/libs/hwui/Properties.h
@@ -285,6 +285,9 @@
static ProfileType getProfileType();
+ // Should be used only by test apps
+ static bool waitForGpuCompletion;
+
private:
static ProfileType sProfileType;
static bool sDisableProfileBars;
diff --git a/libs/hwui/renderthread/EglManager.cpp b/libs/hwui/renderthread/EglManager.cpp
index 78df297..466fef9d 100644
--- a/libs/hwui/renderthread/EglManager.cpp
+++ b/libs/hwui/renderthread/EglManager.cpp
@@ -29,8 +29,6 @@
#define GLES_VERSION 2
-#define WAIT_FOR_GPU_COMPLETION 0
-
// Android-specific addition that is used to show when frames began in systrace
EGLAPI void EGLAPIENTRY eglBeginFrame(EGLDisplay dpy, EGLSurface surface);
@@ -179,7 +177,10 @@
}
void EglManager::createContext() {
- EGLint attribs[] = { EGL_CONTEXT_CLIENT_VERSION, GLES_VERSION, EGL_NONE };
+ EGLint attribs[] = {
+ EGL_CONTEXT_CLIENT_VERSION, GLES_VERSION,
+ EGL_NONE
+ };
mEglContext = eglCreateContext(mEglDisplay, mEglConfig, EGL_NO_CONTEXT, attribs);
LOG_ALWAYS_FATAL_IF(mEglContext == EGL_NO_CONTEXT,
"Failed to create context, error = %s", egl_error_str());
@@ -318,12 +319,10 @@
bool EglManager::swapBuffers(const Frame& frame, const SkRect& screenDirty) {
-#if WAIT_FOR_GPU_COMPLETION
- {
+ if (CC_UNLIKELY(Properties::waitForGpuCompletion)) {
ATRACE_NAME("Finishing GPU work");
fence();
}
-#endif
EGLint rects[4];
frame.map(screenDirty, rects);
diff --git a/libs/hwui/tests/common/TestScene.h b/libs/hwui/tests/common/TestScene.h
index df8d194..706f2ff 100644
--- a/libs/hwui/tests/common/TestScene.h
+++ b/libs/hwui/tests/common/TestScene.h
@@ -37,6 +37,7 @@
public:
struct Options {
int count = 0;
+ int reportFrametimeWeight = 0;
};
template <class T>
diff --git a/libs/hwui/tests/macrobench/TestSceneRunner.cpp b/libs/hwui/tests/macrobench/TestSceneRunner.cpp
index 8261220..a843e92 100644
--- a/libs/hwui/tests/macrobench/TestSceneRunner.cpp
+++ b/libs/hwui/tests/macrobench/TestSceneRunner.cpp
@@ -38,6 +38,30 @@
}
};
+template<class T>
+class ModifiedMovingAverage {
+public:
+ ModifiedMovingAverage(int weight) : mWeight(weight) {}
+
+ T add(T today) {
+ if (!mHasValue) {
+ mAverage = today;
+ } else {
+ mAverage = (((mWeight - 1) * mAverage) + today) / mWeight;
+ }
+ return mAverage;
+ }
+
+ T average() {
+ return mAverage;
+ }
+
+private:
+ bool mHasValue = false;
+ int mWeight;
+ T mAverage;
+};
+
void run(const TestScene::Info& info, const TestScene::Options& opts) {
// Switch to the real display
gDisplay = getBuiltInDisplay();
@@ -67,22 +91,35 @@
proxy->setLightCenter((Vector3){lightX, dp(-200.0f), dp(800.0f)});
// Do a few cold runs then reset the stats so that the caches are all hot
- for (int i = 0; i < 3; i++) {
+ for (int i = 0; i < 5; i++) {
testContext.waitForVsync();
nsecs_t vsync = systemTime(CLOCK_MONOTONIC);
UiFrameInfoBuilder(proxy->frameInfo()).setVsync(vsync, vsync);
proxy->syncAndDrawFrame();
}
+
proxy->resetProfileInfo();
+ proxy->fence();
+
+ ModifiedMovingAverage<double> avgMs(opts.reportFrametimeWeight);
for (int i = 0; i < opts.count; i++) {
testContext.waitForVsync();
-
- ATRACE_NAME("UI-Draw Frame");
nsecs_t vsync = systemTime(CLOCK_MONOTONIC);
- UiFrameInfoBuilder(proxy->frameInfo()).setVsync(vsync, vsync);
- scene->doFrame(i);
- proxy->syncAndDrawFrame();
+ {
+ ATRACE_NAME("UI-Draw Frame");
+ UiFrameInfoBuilder(proxy->frameInfo()).setVsync(vsync, vsync);
+ scene->doFrame(i);
+ proxy->syncAndDrawFrame();
+ }
+ proxy->fence();
+ nsecs_t done = systemTime(CLOCK_MONOTONIC);
+ if (opts.reportFrametimeWeight) {
+ avgMs.add((done - vsync) / 1000000.0);
+ if (i % 10 == 9) {
+ printf("Average frametime %.3fms\n", avgMs.average());
+ }
+ }
}
proxy->dumpProfileInfo(STDOUT_FILENO, 0);
diff --git a/libs/hwui/tests/macrobench/main.cpp b/libs/hwui/tests/macrobench/main.cpp
index 619713c..1616a95 100644
--- a/libs/hwui/tests/macrobench/main.cpp
+++ b/libs/hwui/tests/macrobench/main.cpp
@@ -17,6 +17,7 @@
#include "tests/common/TestScene.h"
#include "protos/hwui.pb.h"
+#include "Properties.h"
#include <getopt.h>
#include <stdio.h>
@@ -25,26 +26,38 @@
#include <unordered_map>
#include <vector>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+
using namespace android;
using namespace android::uirenderer;
using namespace android::uirenderer::test;
-static int gFrameCount = 150;
static int gRepeatCount = 1;
static std::vector<TestScene::Info> gRunTests;
+static TestScene::Options gOpts;
void run(const TestScene::Info& info, const TestScene::Options& opts);
static void printHelp() {
- printf("\
-USAGE: hwuitest [OPTIONS] <TESTNAME>\n\
-\n\
-OPTIONS:\n\
- -c, --count=NUM NUM loops a test should run (example, number of frames)\n\
- -r, --runs=NUM Repeat the test(s) NUM times\n\
- -h, --help Display this help\n\
- --list List all tests\n\
-\n");
+ printf(R"(
+USAGE: hwuitest [OPTIONS] <TESTNAME>
+
+OPTIONS:
+ -c, --count=NUM NUM loops a test should run (example, number of frames)
+ -r, --runs=NUM Repeat the test(s) NUM times
+ -h, --help Display this help
+ --list List all tests
+ --wait-for-gpu Set this to wait for the GPU before producing the
+ next frame. Note that without locked clocks this will
+ pathologically bad performance due to large idle time
+ --report-frametime[=weight] If set, the test will print to stdout the
+ moving average frametime. Weight is optional, default is 10
+ --cpuset=name Adds the test to the specified cpuset before running
+ Not supported on all devices and needs root
+)");
}
static void listTests() {
@@ -77,11 +90,56 @@
}
}
+static void moveToCpuSet(const char* cpusetName) {
+ if (access("/dev/cpuset/tasks", F_OK)) {
+ fprintf(stderr, "don't have access to cpusets, skipping...\n");
+ return;
+ }
+ static const int BUF_SIZE = 100;
+ char buffer[BUF_SIZE];
+
+ if (snprintf(buffer, BUF_SIZE, "/dev/cpuset/%s/tasks", cpusetName) >= BUF_SIZE) {
+ fprintf(stderr, "Error, cpusetName too large to fit in buffer '%s'\n", cpusetName);
+ return;
+ }
+ int fd = open(buffer, O_WRONLY | O_CLOEXEC);
+ if (fd == -1) {
+ fprintf(stderr, "Error opening file %d\n", errno);
+ return;
+ }
+ pid_t pid = getpid();
+
+ int towrite = snprintf(buffer, BUF_SIZE, "%ld", (long) pid);
+ if (towrite >= BUF_SIZE) {
+ fprintf(stderr, "Buffer wasn't large enough?\n");
+ } else {
+ if (write(fd, buffer, towrite) != towrite) {
+ fprintf(stderr, "Failed to write, errno=%d", errno);
+ }
+ }
+ close(fd);
+}
+
+// For options that only exist in long-form. Anything in the
+// 0-255 range is reserved for short options (which just use their ASCII value)
+namespace LongOpts {
+enum {
+ Reserved = 255,
+ List,
+ WaitForGpu,
+ ReportFrametime,
+ CpuSet,
+};
+}
+
static const struct option LONG_OPTIONS[] = {
{ "frames", required_argument, nullptr, 'f' },
{ "repeat", required_argument, nullptr, 'r' },
{ "help", no_argument, nullptr, 'h' },
- { "list", no_argument, nullptr, 'l' },
+ { "list", no_argument, nullptr, LongOpts::List },
+ { "wait-for-gpu", no_argument, nullptr, LongOpts::WaitForGpu },
+ { "report-frametime", optional_argument, nullptr, LongOpts::ReportFrametime },
+ { "cpuset", required_argument, nullptr, LongOpts::CpuSet },
{ 0, 0, 0, 0 }
};
@@ -89,8 +147,6 @@
void parseOptions(int argc, char* argv[]) {
int c;
- // temporary variable
- int count;
bool error = false;
opterr = 0;
@@ -110,31 +166,53 @@
// (although none of the current LONG_OPTIONS do this...)
break;
- case 'l':
+ case LongOpts::List:
listTests();
exit(EXIT_SUCCESS);
break;
case 'c':
- count = atoi(optarg);
- if (!count) {
+ gOpts.count = atoi(optarg);
+ if (!gOpts.count) {
fprintf(stderr, "Invalid frames argument '%s'\n", optarg);
error = true;
- } else {
- gFrameCount = (count > 0 ? count : INT_MAX);
}
break;
case 'r':
- count = atoi(optarg);
- if (!count) {
+ gRepeatCount = atoi(optarg);
+ if (!gRepeatCount) {
fprintf(stderr, "Invalid repeat argument '%s'\n", optarg);
error = true;
} else {
- gRepeatCount = (count > 0 ? count : INT_MAX);
+ gRepeatCount = (gRepeatCount > 0 ? gRepeatCount : INT_MAX);
}
break;
+ case LongOpts::ReportFrametime:
+ if (optarg) {
+ gOpts.reportFrametimeWeight = atoi(optarg);
+ if (!gOpts.reportFrametimeWeight) {
+ fprintf(stderr, "Invalid report frametime weight '%s'\n", optarg);
+ error = true;
+ }
+ } else {
+ gOpts.reportFrametimeWeight = 10;
+ }
+ break;
+
+ case LongOpts::WaitForGpu:
+ Properties::waitForGpuCompletion = true;
+ break;
+
+ case LongOpts::CpuSet:
+ if (!optarg) {
+ error = true;
+ break;
+ }
+ moveToCpuSet(optarg);
+ break;
+
case 'h':
printHelp();
exit(EXIT_SUCCESS);
@@ -172,13 +250,14 @@
}
int main(int argc, char* argv[]) {
+ // set defaults
+ gOpts.count = 150;
+
parseOptions(argc, argv);
- TestScene::Options opts;
- opts.count = gFrameCount;
for (int i = 0; i < gRepeatCount; i++) {
for (auto&& test : gRunTests) {
- run(test, opts);
+ run(test, gOpts);
}
}
printf("Success!\n");
diff --git a/libs/hwui/tests/scripts/prep_volantis.sh b/libs/hwui/tests/scripts/prep_volantis.sh
new file mode 100755
index 0000000..09d4869
--- /dev/null
+++ b/libs/hwui/tests/scripts/prep_volantis.sh
@@ -0,0 +1,54 @@
+#!/bin/bash
+
+# Copyright (C) 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+adb root
+adb wait-for-device
+adb shell stop mpdecision
+adb shell stop perfd
+adb shell stop
+for pid in $( adb shell ps | awk '{ if ( $9 == "surfaceflinger" ) { print $2 } }' ); do
+ adb shell kill $pid
+done
+adb shell setprop debug.egl.traceGpuCompletion 1
+adb shell daemonize surfaceflinger
+sleep 3
+adb shell setprop service.bootanim.exit 1
+
+# cpu possible frequencies
+# 204000 229500 255000 280500 306000 331500 357000 382500 408000 433500 459000
+# 484500 510000 535500 561000 586500 612000 637500 663000 688500 714000 739500
+# 765000 790500 816000 841500 867000 892500 918000 943500 969000 994500 1020000
+# 1122000 1224000 1326000 1428000 1530000 1632000 1734000 1836000 1938000
+# 2014500 2091000 2193000 2295000 2397000 2499000
+
+S=1326000
+echo "set cpu $cpu to $S hz";
+adb shell "echo userspace > /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor"
+adb shell "echo $S > /sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq"
+adb shell "echo $S > /sys/devices/system/cpu/cpu0/cpufreq/scaling_min_freq"
+adb shell "echo $S > /sys/devices/system/cpu/cpu0/cpufreq/scaling_setspeed"
+
+#disable hotplug
+adb shell "echo 0 > /sys/devices/system/cpu/cpuquiet/tegra_cpuquiet/enable"
+
+# gbus possible rates
+# 72000 108000 180000 252000 324000 396000 468000 540000 612000 648000
+# 684000 708000 756000 804000 852000 (kHz)
+
+S=324000000
+echo "set gpu to $s hz"
+adb shell "echo 1 > /d/clock/override.gbus/state"
+adb shell "echo $S > /d/clock/override.gbus/rate"
diff --git a/packages/PrintSpooler/src/com/android/printspooler/model/NotificationController.java b/packages/PrintSpooler/src/com/android/printspooler/model/NotificationController.java
index 82fd512..3dc5d7e 100644
--- a/packages/PrintSpooler/src/com/android/printspooler/model/NotificationController.java
+++ b/packages/PrintSpooler/src/com/android/printspooler/model/NotificationController.java
@@ -158,11 +158,14 @@
builder.addAction(secondAction);
}
- if (printJob.getState() == PrintJobInfo.STATE_STARTED) {
+ if (printJob.getState() == PrintJobInfo.STATE_STARTED
+ || printJob.getState() == PrintJobInfo.STATE_QUEUED) {
float progress = printJob.getProgress();
if (progress >= 0) {
- builder.setProgress(Integer.MAX_VALUE, (int)(Integer.MAX_VALUE * progress),
+ builder.setProgress(Integer.MAX_VALUE, (int) (Integer.MAX_VALUE * progress),
false);
+ } else {
+ builder.setProgress(Integer.MAX_VALUE, 0, true);
}
}
diff --git a/packages/SettingsLib/res/values-bn-rBD/arrays.xml b/packages/SettingsLib/res/values-bn-rBD/arrays.xml
index c26615e..b863934 100644
--- a/packages/SettingsLib/res/values-bn-rBD/arrays.xml
+++ b/packages/SettingsLib/res/values-bn-rBD/arrays.xml
@@ -60,25 +60,25 @@
</string-array>
<string-array name="select_logd_size_titles">
<item msgid="8665206199209698501">"বন্ধ আছে"</item>
- <item msgid="1593289376502312923">"64K"</item>
- <item msgid="487545340236145324">"256K"</item>
- <item msgid="2423528675294333831">"1M"</item>
- <item msgid="180883774509476541">"4M"</item>
- <item msgid="2803199102589126938">"16M"</item>
+ <item msgid="1593289376502312923">"৬৪K"</item>
+ <item msgid="487545340236145324">"২৫৬K"</item>
+ <item msgid="2423528675294333831">"১M"</item>
+ <item msgid="180883774509476541">"৪M"</item>
+ <item msgid="2803199102589126938">"১৬M"</item>
</string-array>
<string-array name="select_logd_size_lowram_titles">
<item msgid="6089470720451068364">"বন্ধ আছে"</item>
- <item msgid="4622460333038586791">"64K"</item>
- <item msgid="2212125625169582330">"256K"</item>
- <item msgid="1704946766699242653">"1M"</item>
+ <item msgid="4622460333038586791">"৬৪K"</item>
+ <item msgid="2212125625169582330">"২৫৬K"</item>
+ <item msgid="1704946766699242653">"১M"</item>
</string-array>
<string-array name="select_logd_size_summaries">
<item msgid="6921048829791179331">"বন্ধ আছে"</item>
- <item msgid="2969458029344750262">"লগ বাফার প্রতি 64K"</item>
- <item msgid="1342285115665698168">"লগ বাফার প্রতি 256K"</item>
- <item msgid="1314234299552254621">"লগ বাফার প্রতি 1M"</item>
- <item msgid="3606047780792894151">"লগ বাফার প্রতি 4M"</item>
- <item msgid="5431354956856655120">"লগ বাফার প্রতি 16M"</item>
+ <item msgid="2969458029344750262">"লগ বাফার প্রতি ৬৪K"</item>
+ <item msgid="1342285115665698168">"লগ বাফার প্রতি ২৫৬K"</item>
+ <item msgid="1314234299552254621">"লগ বাফার প্রতি ১M"</item>
+ <item msgid="3606047780792894151">"লগ বাফার প্রতি ৪M"</item>
+ <item msgid="5431354956856655120">"লগ বাফার প্রতি ১৬M"</item>
</string-array>
<string-array name="window_animation_scale_entries">
<item msgid="8134156599370824081">"অ্যানিমেশন বন্ধ করুন"</item>
diff --git a/packages/SettingsLib/res/values-ky-rKG/arrays.xml b/packages/SettingsLib/res/values-ky-rKG/arrays.xml
index db4979a..34c713e 100644
--- a/packages/SettingsLib/res/values-ky-rKG/arrays.xml
+++ b/packages/SettingsLib/res/values-ky-rKG/arrays.xml
@@ -74,11 +74,11 @@
</string-array>
<string-array name="select_logd_size_summaries">
<item msgid="6921048829791179331">"Өчүк"</item>
- <item msgid="2969458029344750262">"64K каттоо буфери үчүн"</item>
- <item msgid="1342285115665698168">"256K каттоо буфери үчүн"</item>
- <item msgid="1314234299552254621">"1M каттоо буфери үчүн"</item>
- <item msgid="3606047780792894151">"4M каттоо буфери үчүн"</item>
- <item msgid="5431354956856655120">"16M каттоо буфери үчүн"</item>
+ <item msgid="2969458029344750262">"Буфер: 64КБ ашпашы керек"</item>
+ <item msgid="1342285115665698168">"Буфер: 256КБ ашпашы керек"</item>
+ <item msgid="1314234299552254621">"Буфер: 1М ашпашы керек"</item>
+ <item msgid="3606047780792894151">"Буфер: 4М ашпашы керек"</item>
+ <item msgid="5431354956856655120">"Буфер: 16М ашпашы керек"</item>
</string-array>
<string-array name="window_animation_scale_entries">
<item msgid="8134156599370824081">"Анимацияны өчүрүү"</item>
diff --git a/packages/SettingsLib/res/values-ne-rNP/arrays.xml b/packages/SettingsLib/res/values-ne-rNP/arrays.xml
index 707f1dd..7468982 100644
--- a/packages/SettingsLib/res/values-ne-rNP/arrays.xml
+++ b/packages/SettingsLib/res/values-ne-rNP/arrays.xml
@@ -63,8 +63,8 @@
<item msgid="1593289376502312923">"६४के"</item>
<item msgid="487545340236145324">"२५६के"</item>
<item msgid="2423528675294333831">"१एम"</item>
- <item msgid="180883774509476541">"4एम"</item>
- <item msgid="2803199102589126938">"16एम"</item>
+ <item msgid="180883774509476541">"४एम"</item>
+ <item msgid="2803199102589126938">"१६एम"</item>
</string-array>
<string-array name="select_logd_size_lowram_titles">
<item msgid="6089470720451068364">"निष्क्रिय गर्नुहोस्"</item>
@@ -74,11 +74,11 @@
</string-array>
<string-array name="select_logd_size_summaries">
<item msgid="6921048829791179331">"निष्क्रिय गर्नुहोस्"</item>
- <item msgid="2969458029344750262">"64के प्रति लग बफर"</item>
- <item msgid="1342285115665698168">"256के प्रति लग बफर"</item>
- <item msgid="1314234299552254621">"1एम प्रति लग बफर"</item>
- <item msgid="3606047780792894151">"4एम प्रति लग बफर"</item>
- <item msgid="5431354956856655120">"16एम प्रति लग बफर"</item>
+ <item msgid="2969458029344750262">"६४के प्रति लग बफर"</item>
+ <item msgid="1342285115665698168">"२५६के प्रति लग बफर"</item>
+ <item msgid="1314234299552254621">"१एम प्रति लग बफर"</item>
+ <item msgid="3606047780792894151">"४एम प्रति लग बफर"</item>
+ <item msgid="5431354956856655120">"१६एम प्रति लग बफर"</item>
</string-array>
<string-array name="window_animation_scale_entries">
<item msgid="8134156599370824081">"सजीविकरण बन्द"</item>
diff --git a/packages/SettingsLib/res/values-zh-rCN/arrays.xml b/packages/SettingsLib/res/values-zh-rCN/arrays.xml
index 5426d23..d1d8937 100644
--- a/packages/SettingsLib/res/values-zh-rCN/arrays.xml
+++ b/packages/SettingsLib/res/values-zh-rCN/arrays.xml
@@ -58,22 +58,28 @@
<item msgid="3878793616631049349">"仅使用 HDCP 检查 DRM 内容"</item>
<item msgid="45075631231212732">"始终使用 HDCP 检查"</item>
</string-array>
- <!-- no translation found for select_logd_size_titles:0 (8665206199209698501) -->
- <!-- no translation found for select_logd_size_titles:1 (1593289376502312923) -->
- <!-- no translation found for select_logd_size_titles:2 (487545340236145324) -->
- <!-- no translation found for select_logd_size_titles:3 (2423528675294333831) -->
- <!-- no translation found for select_logd_size_titles:4 (180883774509476541) -->
- <!-- no translation found for select_logd_size_titles:5 (2803199102589126938) -->
- <!-- no translation found for select_logd_size_lowram_titles:0 (6089470720451068364) -->
- <!-- no translation found for select_logd_size_lowram_titles:1 (4622460333038586791) -->
- <!-- no translation found for select_logd_size_lowram_titles:2 (2212125625169582330) -->
- <!-- no translation found for select_logd_size_lowram_titles:3 (1704946766699242653) -->
- <!-- no translation found for select_logd_size_summaries:0 (6921048829791179331) -->
- <!-- no translation found for select_logd_size_summaries:1 (2969458029344750262) -->
- <!-- no translation found for select_logd_size_summaries:2 (1342285115665698168) -->
- <!-- no translation found for select_logd_size_summaries:3 (1314234299552254621) -->
- <!-- no translation found for select_logd_size_summaries:4 (3606047780792894151) -->
- <!-- no translation found for select_logd_size_summaries:5 (5431354956856655120) -->
+ <string-array name="select_logd_size_titles">
+ <item msgid="8665206199209698501">"关闭"</item>
+ <item msgid="1593289376502312923">"64K"</item>
+ <item msgid="487545340236145324">"256K"</item>
+ <item msgid="2423528675294333831">"1M"</item>
+ <item msgid="180883774509476541">"4M"</item>
+ <item msgid="2803199102589126938">"16M"</item>
+ </string-array>
+ <string-array name="select_logd_size_lowram_titles">
+ <item msgid="6089470720451068364">"关闭"</item>
+ <item msgid="4622460333038586791">"64K"</item>
+ <item msgid="2212125625169582330">"256K"</item>
+ <item msgid="1704946766699242653">"1M"</item>
+ </string-array>
+ <string-array name="select_logd_size_summaries">
+ <item msgid="6921048829791179331">"关闭"</item>
+ <item msgid="2969458029344750262">"每个日志缓冲区 64K"</item>
+ <item msgid="1342285115665698168">"每个日志缓冲区 256K"</item>
+ <item msgid="1314234299552254621">"每个日志缓冲区 1M"</item>
+ <item msgid="3606047780792894151">"每个日志缓冲区 4M"</item>
+ <item msgid="5431354956856655120">"每个日志缓冲区 16M"</item>
+ </string-array>
<string-array name="window_animation_scale_entries">
<item msgid="8134156599370824081">"关闭动画"</item>
<item msgid="6624864048416710414">"动画缩放 0.5x"</item>
diff --git a/packages/SystemUI/res/layout/assist_orb.xml b/packages/SystemUI/res/layout/assist_orb.xml
index ab0a0a5b..0036ed6 100644
--- a/packages/SystemUI/res/layout/assist_orb.xml
+++ b/packages/SystemUI/res/layout/assist_orb.xml
@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
-/* apps/common/assets/default/default/skins/StatusBar.xml
**
** Copyright 2012, The Android Open Source Project
**
diff --git a/packages/SystemUI/res/layout/mobile_signal_group.xml b/packages/SystemUI/res/layout/mobile_signal_group.xml
index 6ae5cf3..a20ec8e 100644
--- a/packages/SystemUI/res/layout/mobile_signal_group.xml
+++ b/packages/SystemUI/res/layout/mobile_signal_group.xml
@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
-/* apps/common/assets/default/default/skins/StatusBar.xml
**
** Copyright 2011, The Android Open Source Project
**
diff --git a/packages/SystemUI/res/layout/navigation_bar.xml b/packages/SystemUI/res/layout/navigation_bar.xml
index d58664f..8498a4f 100644
--- a/packages/SystemUI/res/layout/navigation_bar.xml
+++ b/packages/SystemUI/res/layout/navigation_bar.xml
@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
-/* apps/common/assets/default/default/skins/StatusBar.xml
**
** Copyright 2011, The Android Open Source Project
**
diff --git a/packages/SystemUI/res/layout/recents_task_view.xml b/packages/SystemUI/res/layout/recents_task_view.xml
index 2b82b05..5b44c17 100644
--- a/packages/SystemUI/res/layout/recents_task_view.xml
+++ b/packages/SystemUI/res/layout/recents_task_view.xml
@@ -34,9 +34,10 @@
android:layout_gravity="bottom|right"
android:layout_marginRight="15dp"
android:layout_marginBottom="15dp"
- android:translationZ="2dp"
+ android:translationZ="4dp"
android:contentDescription="@string/recents_lock_to_app_button_label"
- android:background="@drawable/recents_lock_to_task_button_bg">
+ android:background="@drawable/recents_lock_to_task_button_bg"
+ android:visibility="invisible">
<ImageView
android:layout_width="@dimen/recents_lock_to_app_icon_size"
android:layout_height="@dimen/recents_lock_to_app_icon_size"
diff --git a/packages/SystemUI/res/layout/signal_cluster_view.xml b/packages/SystemUI/res/layout/signal_cluster_view.xml
index f8bd6fd..198e658 100644
--- a/packages/SystemUI/res/layout/signal_cluster_view.xml
+++ b/packages/SystemUI/res/layout/signal_cluster_view.xml
@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
-/* apps/common/assets/default/default/skins/StatusBar.xml
**
** Copyright 2011, The Android Open Source Project
**
diff --git a/packages/SystemUI/res/layout/status_bar.xml b/packages/SystemUI/res/layout/status_bar.xml
index a5b3a83..aaa5a09 100644
--- a/packages/SystemUI/res/layout/status_bar.xml
+++ b/packages/SystemUI/res/layout/status_bar.xml
@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
-/* apps/common/assets/default/default/skins/StatusBar.xml
**
** Copyright 2006, The Android Open Source Project
**
diff --git a/packages/SystemUI/res/layout/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index bfa13e2..12cf137 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
-/* apps/common/assets/default/default/skins/StatusBar.xml
**
** Copyright 2006, The Android Open Source Project
**
diff --git a/packages/SystemUI/res/layout/status_bar_no_recent_apps.xml b/packages/SystemUI/res/layout/status_bar_no_recent_apps.xml
index 1675773..adfaed13 100644
--- a/packages/SystemUI/res/layout/status_bar_no_recent_apps.xml
+++ b/packages/SystemUI/res/layout/status_bar_no_recent_apps.xml
@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
-/* apps/common/assets/default/default/skins/StatusBar.xml
**
** Copyright 2011, The Android Open Source Project
**
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSIconView.java b/packages/SystemUI/src/com/android/systemui/qs/QSIconView.java
index b56ad76..01eb5f9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSIconView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSIconView.java
@@ -75,7 +75,8 @@
iv.setTag(R.id.qs_icon_tag, state.icon);
if (d instanceof Animatable) {
Animatable a = (Animatable) d;
- if (state.icon instanceof QSTile.AnimationIcon && !iv.isShown()) {
+ a.start();
+ if (!iv.isShown()) {
a.stop(); // skip directly to end state
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTile.java b/packages/SystemUI/src/com/android/systemui/qs/QSTile.java
index 39f0c55..1a36abd 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTile.java
@@ -19,8 +19,6 @@
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
-import android.graphics.drawable.Animatable;
-import android.graphics.drawable.AnimatedVectorDrawable;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.os.Looper;
@@ -29,9 +27,20 @@
import android.util.SparseArray;
import android.view.View;
import android.view.ViewGroup;
-
import com.android.systemui.qs.QSTile.State;
-import com.android.systemui.statusbar.policy.*;
+import com.android.systemui.statusbar.policy.BatteryController;
+import com.android.systemui.statusbar.policy.BluetoothController;
+import com.android.systemui.statusbar.policy.CastController;
+import com.android.systemui.statusbar.policy.FlashlightController;
+import com.android.systemui.statusbar.policy.HotspotController;
+import com.android.systemui.statusbar.policy.KeyguardMonitor;
+import com.android.systemui.statusbar.policy.Listenable;
+import com.android.systemui.statusbar.policy.LocationController;
+import com.android.systemui.statusbar.policy.NetworkController;
+import com.android.systemui.statusbar.policy.RotationLockController;
+import com.android.systemui.statusbar.policy.UserInfoController;
+import com.android.systemui.statusbar.policy.UserSwitcherController;
+import com.android.systemui.statusbar.policy.ZenModeController;
import java.util.Collection;
import java.util.Objects;
@@ -389,11 +398,7 @@
@Override
public Drawable getDrawable(Context context) {
- Drawable d = context.getDrawable(mResId);
- if (d instanceof Animatable) {
- ((Animatable) d).start();
- }
- return d;
+ return context.getDrawable(mResId);
}
@Override
@@ -408,41 +413,14 @@
}
protected class AnimationIcon extends ResourceIcon {
- private boolean mAllowAnimation;
-
public AnimationIcon(int resId) {
super(resId);
}
- public void setAllowAnimation(boolean allowAnimation) {
- mAllowAnimation = allowAnimation;
- }
-
@Override
public Drawable getDrawable(Context context) {
// workaround: get a clean state for every new AVD
- final AnimatedVectorDrawable d = (AnimatedVectorDrawable) context.getDrawable(mResId)
- .getConstantState().newDrawable();
- d.start();
- if (mAllowAnimation) {
- mAllowAnimation = false;
- } else {
- d.stop(); // skip directly to end state
- }
- return d;
- }
- }
-
- protected enum UserBoolean {
- USER_TRUE(true, true),
- USER_FALSE(true, false),
- BACKGROUND_TRUE(false, true),
- BACKGROUND_FALSE(false, false);
- public final boolean value;
- public final boolean userInitiated;
- private UserBoolean(boolean userInitiated, boolean value) {
- this.value = value;
- this.userInitiated = userInitiated;
+ return context.getDrawable(mResId).getConstantState().newDrawable();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSTileServiceWrapper.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileServiceWrapper.java
deleted file mode 100644
index a5e1fd5..0000000
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileServiceWrapper.java
+++ /dev/null
@@ -1,76 +0,0 @@
-package com.android.systemui.qs;
-
-import android.os.IBinder;
-import android.service.quicksettings.IQSTileService;
-import android.service.quicksettings.Tile;
-import android.util.Log;
-
-
-public class QSTileServiceWrapper implements IQSTileService {
- private static final String TAG = "IQSTileServiceWrapper";
-
- private final IQSTileService mService;
-
- public QSTileServiceWrapper(IQSTileService service) {
- mService = service;
- }
-
- @Override
- public IBinder asBinder() {
- return mService.asBinder();
- }
-
- @Override
- public void setQSTile(Tile tile) {
- try {
- mService.setQSTile(tile);
- } catch (Exception e) {
- Log.d(TAG, "Caught exception from QSTileService", e);
- }
- }
-
- @Override
- public void onTileAdded() {
- try {
- mService.onTileAdded();
- } catch (Exception e) {
- Log.d(TAG, "Caught exception from QSTileService", e);
- }
- }
-
- @Override
- public void onTileRemoved() {
- try {
- mService.onTileRemoved();
- } catch (Exception e) {
- Log.d(TAG, "Caught exception from QSTileService", e);
- }
- }
-
- @Override
- public void onStartListening() {
- try {
- mService.onStartListening();
- } catch (Exception e) {
- Log.d(TAG, "Caught exception from QSTileService", e);
- }
- }
-
- @Override
- public void onStopListening() {
- try {
- mService.onStopListening();
- } catch (Exception e) {
- Log.d(TAG, "Caught exception from QSTileService", e);
- }
- }
-
- @Override
- public void onClick(IBinder token) {
- try {
- mService.onClick(token);
- } catch (Exception e) {
- Log.d(TAG, "Caught exception from QSTileService", e);
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/CustomQSPanel.java b/packages/SystemUI/src/com/android/systemui/qs/customize/CustomQSPanel.java
index 87c29735..5ac63bc 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/CustomQSPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/CustomQSPanel.java
@@ -35,8 +35,8 @@
import com.android.systemui.R;
import com.android.systemui.qs.QSPanel;
import com.android.systemui.qs.QSTile;
-import com.android.systemui.qs.QSTileServiceWrapper;
-import com.android.systemui.qs.tiles.CustomTile;
+import com.android.systemui.qs.external.QSTileServiceWrapper;
+import com.android.systemui.qs.external.CustomTile;
import com.android.systemui.statusbar.phone.QSTileHost;
import com.android.systemui.tuner.TunerService;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
index 6706c7a..a6a7143 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/customize/TileAdapter.java
@@ -41,7 +41,7 @@
import com.android.systemui.R;
import com.android.systemui.qs.QSTile;
import com.android.systemui.qs.QSTile.Icon;
-import com.android.systemui.qs.tiles.CustomTile;
+import com.android.systemui.qs.external.CustomTile;
import com.android.systemui.statusbar.phone.QSTileHost;
import com.android.systemui.tuner.QSPagingSwitch;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CustomTile.java b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
similarity index 71%
rename from packages/SystemUI/src/com/android/systemui/qs/tiles/CustomTile.java
rename to packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
index bb74f34..e622e11 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CustomTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
@@ -11,23 +11,17 @@
* 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.
+ * limitations under the License
*/
+package com.android.systemui.qs.external;
-package com.android.systemui.qs.tiles;
-
-import android.app.ActivityManager;
-import android.app.Service;
import android.content.ComponentName;
-import android.content.Intent;
-import android.content.ServiceConnection;
import android.content.pm.PackageManager;
import android.content.pm.ServiceInfo;
import android.graphics.drawable.Drawable;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
-import android.os.UserHandle;
import android.service.quicksettings.IQSTileService;
import android.service.quicksettings.Tile;
import android.util.Log;
@@ -36,7 +30,6 @@
import android.view.WindowManagerGlobal;
import com.android.internal.logging.MetricsLogger;
import com.android.systemui.qs.QSTile;
-import com.android.systemui.qs.QSTileServiceWrapper;
import com.android.systemui.statusbar.phone.QSTileHost;
public class CustomTile extends QSTile<QSTile.State> {
@@ -52,8 +45,9 @@
private final Tile mTile;
private final IWindowManager mWindowManager;
private final IBinder mToken = new Binder();
+ private final IQSTileService mService;
+ private final TileServiceManager mServiceManager;
- private QSTileServiceWrapper mService;
private boolean mListening;
private boolean mBound;
private boolean mIsTokenGranted;
@@ -63,7 +57,9 @@
super(host);
mWindowManager = WindowManagerGlobal.getWindowManagerService();
mComponent = ComponentName.unflattenFromString(action);
- mTile = new Tile(mComponent, host);
+ mServiceManager = host.getTileServices().getTileWrapper(this);
+ mService = mServiceManager.getTileService();
+ mTile = new Tile(mComponent, host.getTileServices());
try {
PackageManager pm = mContext.getPackageManager();
ServiceInfo info = pm.getServiceInfo(mComponent, 0);
@@ -96,42 +92,32 @@
public void setListening(boolean listening) {
if (mListening == listening) return;
mListening = listening;
- if (listening) {
- mHandler.removeCallbacks(mUnbind);
- if (!mBound) {
- // TODO: Guarantee re-bind on user-switch.
- mContext.bindServiceAsUser(new Intent().setComponent(mComponent),
- mServiceConnection, Service.BIND_AUTO_CREATE,
- new UserHandle(ActivityManager.getCurrentUser()));
- mBound = true;
+ try {
+ if (listening) {
+ mServiceManager.setBindRequested(true);
+ mService.setQSTile(mTile);
+ mService.onStartListening();
} else {
- if (mService != null) {
- mService.onStartListening();
- } else {
- Log.d(TAG, "Can't start service listening");
- }
- }
- } else {
- if (mService != null) {
mService.onStopListening();
- }
- if (mIsTokenGranted && !mIsShowingDialog) {
- try {
- if (DEBUG) Log.d(TAG, "Removing token");
- mWindowManager.removeWindowToken(mToken);
- } catch (RemoteException e) {
+ if (mIsTokenGranted && !mIsShowingDialog) {
+ try {
+ if (DEBUG) Log.d(TAG, "Removing token");
+ mWindowManager.removeWindowToken(mToken);
+ } catch (RemoteException e) {
+ }
+ mIsTokenGranted = false;
}
- mIsTokenGranted = false;
+ mIsShowingDialog = false;
+ mServiceManager.setBindRequested(false);
}
- mIsShowingDialog = false;
- mHandler.postDelayed(mUnbind, UNBIND_DELAY);
+ } catch (RemoteException e) {
+ // Called through wrapper, won't happen here.
}
}
@Override
protected void handleDestroy() {
super.handleDestroy();
- mHandler.removeCallbacks(mUnbind);
if (mIsTokenGranted) {
try {
if (DEBUG) Log.d(TAG, "Removing token");
@@ -139,7 +125,6 @@
} catch (RemoteException e) {
}
}
- mUnbind.run();
}
@Override
@@ -161,7 +146,11 @@
mIsTokenGranted = true;
} catch (RemoteException e) {
}
- mService.onClick(mToken);
+ try {
+ mService.onClick(mToken);
+ } catch (RemoteException e) {
+ // Called through wrapper, won't happen here.
+ }
} else {
Log.e(TAG, "Click with no service " + getTileSpec());
}
@@ -187,34 +176,9 @@
@Override
public int getMetricsCategory() {
- return MetricsLogger.QS_INTENT;
+ return MetricsLogger.QS_CUSTOM;
}
- private final ServiceConnection mServiceConnection = new ServiceConnection() {
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- mService = new QSTileServiceWrapper(IQSTileService.Stub.asInterface(service));
- if (mListening) {
- mService.setQSTile(mTile);
- mService.onStartListening();
- } else {
- mService.onStopListening();
- }
- }
-
- @Override
- public void onServiceDisconnected(ComponentName name) {
- }
- };
-
- private final Runnable mUnbind = new Runnable() {
- @Override
- public void run() {
- mContext.unbindService(mServiceConnection);
- mBound = false;
- }
- };
-
public static ComponentName getComponentFromSpec(String spec) {
final String action = spec.substring(PREFIX.length(), spec.length() - 1);
if (action.isEmpty()) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/QSTileServiceWrapper.java b/packages/SystemUI/src/com/android/systemui/qs/external/QSTileServiceWrapper.java
new file mode 100644
index 0000000..d656686
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/QSTileServiceWrapper.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+package com.android.systemui.qs.external;
+
+import android.os.IBinder;
+import android.service.quicksettings.IQSTileService;
+import android.service.quicksettings.Tile;
+import android.util.Log;
+
+
+public class QSTileServiceWrapper {
+ private static final String TAG = "IQSTileServiceWrapper";
+
+ private final IQSTileService mService;
+
+ public QSTileServiceWrapper(IQSTileService service) {
+ mService = service;
+ }
+
+ public IBinder asBinder() {
+ return mService.asBinder();
+ }
+
+ public boolean setQSTile(Tile tile) {
+ try {
+ mService.setQSTile(tile);
+ return true;
+ } catch (Exception e) {
+ Log.d(TAG, "Caught exception from TileService", e);
+ return false;
+ }
+ }
+
+ public boolean onTileAdded() {
+ try {
+ mService.onTileAdded();
+ return true;
+ } catch (Exception e) {
+ Log.d(TAG, "Caught exception from TileService", e);
+ return false;
+ }
+ }
+
+ public boolean onTileRemoved() {
+ try {
+ mService.onTileRemoved();
+ return true;
+ } catch (Exception e) {
+ Log.d(TAG, "Caught exception from TileService", e);
+ return false;
+ }
+ }
+
+ public boolean onStartListening() {
+ try {
+ mService.onStartListening();
+ return true;
+ } catch (Exception e) {
+ Log.d(TAG, "Caught exception from TileService", e);
+ return false;
+ }
+ }
+
+ public boolean onStopListening() {
+ try {
+ mService.onStopListening();
+ return true;
+ } catch (Exception e) {
+ Log.d(TAG, "Caught exception from TileService", e);
+ return false;
+ }
+ }
+
+ public boolean onClick(IBinder token) {
+ try {
+ mService.onClick(token);
+ return true;
+ } catch (Exception e) {
+ Log.d(TAG, "Caught exception from TileService", e);
+ return false;
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java
new file mode 100644
index 0000000..500ee19
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java
@@ -0,0 +1,331 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+package com.android.systemui.qs.external;
+
+import android.app.AppGlobals;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.ServiceConnection;
+import android.content.pm.PackageManager;
+import android.content.pm.ServiceInfo;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import android.service.quicksettings.IQSTileService;
+import android.service.quicksettings.Tile;
+import android.support.annotation.VisibleForTesting;
+import android.util.ArraySet;
+import android.util.Log;
+import libcore.util.Objects;
+
+import java.util.Set;
+
+/**
+ * Manages the lifecycle of a TileService.
+ * <p>
+ * Will keep track of all calls on the IQSTileService interface and will relay those calls to the
+ * TileService as soon as it is bound. It will only bind to the service when it is allowed to
+ * ({@link #setBindService(boolean)}) and when the service is available.
+ */
+public class TileLifecycleManager extends BroadcastReceiver implements
+ IQSTileService, ServiceConnection, IBinder.DeathRecipient {
+ public static final boolean DEBUG = false;
+
+ private static final String TAG = "TileLifecycleManager";
+
+ private static final int MSG_ON_ADDED = 0;
+ private static final int MSG_ON_REMOVED = 1;
+ private static final int MSG_ON_CLICK = 2;
+
+ // Bind retry control.
+ private static final int MAX_BIND_RETRIES = 5;
+ private static final int BIND_RETRY_DELAY = 1000;
+
+ private final Context mContext;
+ private final Handler mHandler;
+ private final Intent mIntent;
+ private final UserHandle mUser;
+
+ private Set<Integer> mQueuedMessages = new ArraySet<>();
+ private QSTileServiceWrapper mWrapper;
+ private boolean mListening;
+ private Tile mTile;
+ private IBinder mClickBinder;
+
+ private int mBindTryCount;
+ private boolean mBound;
+ @VisibleForTesting
+ boolean mReceiverRegistered;
+
+ public TileLifecycleManager(Handler handler, Context context, Intent intent, UserHandle user) {
+ mContext = context;
+ mHandler = handler;
+ mIntent = intent;
+ mUser = user;
+ }
+
+ public boolean hasPendingClick() {
+ synchronized (mQueuedMessages) {
+ return mQueuedMessages.contains(MSG_ON_CLICK);
+ }
+ }
+
+ public void setBindService(boolean bind) {
+ mBound = bind;
+ if (bind) {
+ if (mBindTryCount == MAX_BIND_RETRIES) {
+ // Too many failures, give up on this tile until an update.
+ startPackageListening();
+ return;
+ }
+ if (!checkComponentState()) {
+ return;
+ }
+ if (DEBUG) Log.d(TAG, "Binding service " + mIntent);
+ mBindTryCount++;
+ mContext.bindServiceAsUser(mIntent, this,
+ Context.BIND_AUTO_CREATE | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE,
+ mUser);
+ } else {
+ if (DEBUG) Log.d(TAG, "Unbinding service " + mIntent);
+ // Give it another chance next time it needs to be bound, out of kindness.
+ mBindTryCount = 0;
+ mContext.unbindService(this);
+ }
+ }
+
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ if (DEBUG) Log.d(TAG, "onServiceConnected " + name);
+ // Got a connection, set the binding count to 0.
+ mBindTryCount = 0;
+ mWrapper = new QSTileServiceWrapper(Stub.asInterface(service));
+ try {
+ service.linkToDeath(this, 0);
+ } catch (RemoteException e) {
+ }
+ handlePendingMessages();
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ if (DEBUG) Log.d(TAG, "onServiceDisconnected " + name);
+ mWrapper = null;
+ }
+
+ private void handlePendingMessages() {
+ // This ordering is laid out manually to make sure we preserve the TileService
+ // lifecycle.
+ ArraySet<Integer> queue;
+ synchronized (mQueuedMessages) {
+ queue = new ArraySet<>(mQueuedMessages);
+ mQueuedMessages.clear();
+ }
+ if (queue.contains(MSG_ON_ADDED)) {
+ if (DEBUG) Log.d(TAG, "Handling pending onAdded");
+ onTileAdded();
+ }
+ if (mListening) {
+ if (DEBUG) Log.d(TAG, "Handling pending onStartListening");
+ setQSTile(mTile);
+ onStartListening();
+ }
+ if (queue.contains(MSG_ON_CLICK)) {
+ if (DEBUG) Log.d(TAG, "Handling pending onClick");
+ if (!mListening) {
+ Log.w(TAG, "Managed to get click on non-listening state...");
+ // Skipping click since lost click privileges.
+ } else {
+ onClick(mClickBinder);
+ }
+ }
+ if (queue.contains(MSG_ON_REMOVED)) {
+ if (DEBUG) Log.d(TAG, "Handling pending onRemoved");
+ if (mListening) {
+ Log.w(TAG, "Managed to get remove in listening state...");
+ onStopListening();
+ }
+ onTileRemoved();
+ }
+ }
+
+ public void handleDestroy() {
+ if (DEBUG) Log.d(TAG, "handleDestroy");
+ if (mReceiverRegistered) {
+ stopPackageListening();
+ }
+ }
+
+ private void handleDeath() {
+ if (mWrapper == null) return;
+ mWrapper = null;
+ if (!mBound) return;
+ if (DEBUG) Log.d(TAG, "handleDeath");
+ if (checkComponentState()) {
+ mHandler.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ if (mBound) {
+ // Retry binding.
+ setBindService(true);
+ }
+ }
+ }, BIND_RETRY_DELAY);
+ }
+ }
+
+ @Override
+ public void setQSTile(Tile tile) {
+ if (DEBUG) Log.d(TAG, "setQSTile " + tile);
+ mTile = tile;
+ if (mWrapper != null && !mWrapper.setQSTile(tile)) {
+ handleDeath();
+ }
+ }
+
+ private boolean checkComponentState() {
+ PackageManager pm = mContext.getPackageManager();
+ if (!isPackageAvailable(pm) || !isComponentAvailable(pm)) {
+ startPackageListening();
+ return false;
+ }
+ return true;
+ }
+
+ private void startPackageListening() {
+ if (DEBUG) Log.d(TAG, "startPackageListening");
+ IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
+ filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
+ filter.addDataScheme("package");
+ mContext.registerReceiverAsUser(this, mUser, filter, null, mHandler);
+ mReceiverRegistered = true;
+ }
+
+ private void stopPackageListening() {
+ if (DEBUG) Log.d(TAG, "stopPackageListening");
+ mContext.unregisterReceiver(this);
+ mReceiverRegistered = false;
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (DEBUG) Log.d(TAG, "onReceive: " + intent);
+ Uri data = intent.getData();
+ String pkgName = data.getEncodedSchemeSpecificPart();
+ if (!Objects.equal(pkgName, mIntent.getComponent().getPackageName())) {
+ return;
+ }
+ stopPackageListening();
+ if (mBound) {
+ // Trying to bind again will check the state of the package before bothering to bind.
+ if (DEBUG) Log.d(TAG, "Trying to rebind");
+ setBindService(true);
+ }
+ }
+
+ private boolean isComponentAvailable(PackageManager pm) {
+ String packageName = mIntent.getComponent().getPackageName();
+ try {
+ ServiceInfo si = AppGlobals.getPackageManager().getServiceInfo(mIntent.getComponent(),
+ 0, mUser.getIdentifier());
+ if (DEBUG && si == null) Log.d(TAG, "Can't find component " + mIntent.getComponent());
+ return si != null;
+ } catch (RemoteException e) {
+ // Shouldn't happen.
+ }
+ return false;
+ }
+
+ private boolean isPackageAvailable(PackageManager pm) {
+ String packageName = mIntent.getComponent().getPackageName();
+ try {
+ pm.getPackageInfoAsUser(packageName, 0, mUser.getIdentifier());
+ return true;
+ } catch (PackageManager.NameNotFoundException e) {
+ if (DEBUG) Log.d(TAG, "Package not available: " + packageName, e);
+ else Log.d(TAG, "Package not available: " + packageName);
+ }
+ return false;
+ }
+
+ private void queueMessage(int message) {
+ synchronized (mQueuedMessages) {
+ mQueuedMessages.add(message);
+ }
+ }
+
+ @Override
+ public void onTileAdded() {
+ if (DEBUG) Log.d(TAG, "onTileAdded");
+ if (mWrapper == null || !mWrapper.onTileAdded()) {
+ queueMessage(MSG_ON_ADDED);
+ handleDeath();
+ }
+ }
+
+ @Override
+ public void onTileRemoved() {
+ if (DEBUG) Log.d(TAG, "onTileRemoved");
+ if (mWrapper == null || !mWrapper.onTileRemoved()) {
+ queueMessage(MSG_ON_REMOVED);
+ handleDeath();
+ }
+ }
+
+ @Override
+ public void onStartListening() {
+ if (DEBUG) Log.d(TAG, "onStartListening");
+ mListening = true;
+ if (mWrapper != null && !mWrapper.onStartListening()) {
+ handleDeath();
+ }
+ }
+
+ @Override
+ public void onStopListening() {
+ if (DEBUG) Log.d(TAG, "onStopListening");
+ mListening = false;
+ if (mWrapper != null && !mWrapper.onStopListening()) {
+ handleDeath();
+ }
+ }
+
+ @Override
+ public void onClick(IBinder iBinder) {
+ if (DEBUG) Log.d(TAG, "onClick " + iBinder);
+ if (mWrapper == null || !mWrapper.onClick(iBinder)) {
+ mClickBinder = iBinder;
+ queueMessage(MSG_ON_CLICK);
+ handleDeath();
+ }
+ }
+
+ @Override
+ public IBinder asBinder() {
+ return mWrapper != null ? mWrapper.asBinder() : null;
+ }
+
+ @Override
+ public void binderDied() {
+ if (DEBUG) Log.d(TAG, "binderDeath");
+ handleDeath();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceManager.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceManager.java
new file mode 100644
index 0000000..ca589df
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceManager.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+package com.android.systemui.qs.external;
+
+import android.app.ActivityManager;
+import android.content.ComponentName;
+import android.content.Intent;
+import android.os.Handler;
+import android.os.UserHandle;
+import android.service.quicksettings.IQSTileService;
+import android.support.annotation.VisibleForTesting;
+import android.util.Log;
+
+/**
+ * Manages the priority which lets {@link TileServices} make decisions about which tiles
+ * to bind. Also holds on to and manages the {@link TileLifecycleManager}, informing it
+ * of when it is allowed to bind based on decisions frome the {@link TileServices}.
+ */
+public class TileServiceManager {
+
+ private static final long MIN_BIND_TIME = 5000;
+ private static final long UNBIND_DELAY = 30000;
+
+ public static final boolean DEBUG = true;
+
+ private static final String TAG = "TileServiceManager";
+
+ private final TileServices mServices;
+ private final TileLifecycleManager mStateManager;
+ private final Handler mHandler;
+ private boolean mBindRequested;
+ private boolean mBindAllowed;
+ private boolean mBound;
+ private int mPriority;
+ private boolean mJustBound;
+ private long mLastUpdate;
+
+ TileServiceManager(TileServices tileServices, Handler handler, ComponentName component) {
+ this(tileServices, handler, new TileLifecycleManager(handler,
+ tileServices.getContext(), new Intent().setComponent(component),
+ new UserHandle(ActivityManager.getCurrentUser())));
+ }
+
+ @VisibleForTesting
+ TileServiceManager(TileServices tileServices, Handler handler,
+ TileLifecycleManager tileLifecycleManager) {
+ mServices = tileServices;
+ mHandler = handler;
+ mStateManager = tileLifecycleManager;
+ }
+
+ public IQSTileService getTileService() {
+ return mStateManager;
+ }
+
+ public void setBindRequested(boolean bindRequested) {
+ if (mBindRequested == bindRequested) return;
+ mBindRequested = bindRequested;
+ if (mBindAllowed && mBindRequested && !mBound) {
+ bindService();
+ } else {
+ mServices.recalculateBindAllowance();
+ }
+ if (mBound && !mBindRequested) {
+ // TODO: Schedule unbind.
+ }
+ }
+
+ public void setLastUpdate(long lastUpdate) {
+ mLastUpdate = lastUpdate;
+ mServices.recalculateBindAllowance();
+ }
+
+ public void handleDestroy() {
+ mStateManager.handleDestroy();
+ }
+
+ public void setBindAllowed(boolean allowed) {
+ if (mBindAllowed == allowed) return;
+ mBindAllowed = allowed;
+ if (!mBindAllowed && mBound) {
+ unbindService();
+ } else if (mBindAllowed && mBindRequested && !mBound) {
+ bindService();
+ }
+ }
+
+ private void bindService() {
+ if (mBound) {
+ Log.e(TAG, "Service already bound");
+ return;
+ }
+ mBound = true;
+ mJustBound = true;
+ mHandler.postDelayed(mJustBoundOver, MIN_BIND_TIME);
+ mStateManager.setBindService(true);
+ }
+
+ private void unbindService() {
+ if (!mBound) {
+ Log.e(TAG, "Service not bound");
+ return;
+ }
+ mBound = false;
+ mJustBound = false;
+ mStateManager.setBindService(false);
+ }
+
+ public void calculateBindPriority(long currentTime) {
+ if (mStateManager.hasPendingClick()) {
+ // Pending click is the most important thing, need to put this service at the top of
+ // the list to be bound.
+ mPriority = Integer.MAX_VALUE;
+ } else if (mJustBound) {
+ // If we just bound, lets not thrash on binding/unbinding too much, this is second most
+ // important.
+ mPriority = Integer.MAX_VALUE - 1;
+ } else if (!mBindRequested) {
+ // Don't care about binding right now, put us last.
+ mPriority = Integer.MIN_VALUE;
+ } else {
+ // Order based on whether this was just updated.
+ long timeSinceUpdate = currentTime - mLastUpdate;
+ // Fit compare into integer space for simplicity. Make sure to leave MAX_VALUE and
+ // MAX_VALUE - 1 for the more important states above.
+ if (timeSinceUpdate > Integer.MAX_VALUE - 2) {
+ mPriority = Integer.MAX_VALUE - 2;
+ } else {
+ mPriority = (int) timeSinceUpdate;
+ }
+ }
+ }
+
+ public int getBindPriority() {
+ return mPriority;
+ }
+
+ @VisibleForTesting
+ final Runnable mJustBoundOver = new Runnable() {
+ @Override
+ public void run() {
+ mJustBound = false;
+ mServices.recalculateBindAllowance();
+ }
+ };
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java
new file mode 100644
index 0000000..d110d97
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+package com.android.systemui.qs.external;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.Looper;
+import android.service.quicksettings.IQSService;
+import android.service.quicksettings.Tile;
+import android.util.ArrayMap;
+import com.android.systemui.statusbar.phone.QSTileHost;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+
+/**
+ * Runs the day-to-day operations of which tiles should be bound and when.
+ */
+public class TileServices extends IQSService.Stub {
+ static final int DEFAULT_MAX_BOUND = 3;
+ static final int REDUCED_MAX_BOUND = 1;
+
+ private final ArrayMap<CustomTile, TileServiceManager> mServices = new ArrayMap<>();
+ private final ArrayMap<ComponentName, CustomTile> mTiles = new ArrayMap<>();
+ private final Context mContext;
+ private final Handler mHandler;
+ private final QSTileHost mHost;
+
+ private int mMaxBound = DEFAULT_MAX_BOUND;
+
+ public TileServices(QSTileHost host, Looper looper) {
+ mHost = host;
+ mContext = mHost.getContext();
+ mHandler = new Handler(looper);
+ }
+
+ public Context getContext() {
+ return mContext;
+ }
+
+ public TileServiceManager getTileWrapper(CustomTile tile) {
+ ComponentName component = tile.getComponent();
+ TileServiceManager service = onCreateTileService(component);
+ synchronized (mServices) {
+ mServices.put(tile, service);
+ mTiles.put(component, tile);
+ }
+ return service;
+ }
+
+ protected TileServiceManager onCreateTileService(ComponentName component) {
+ return new TileServiceManager(this, mHandler, component);
+ }
+
+ public void freeService(CustomTile tile, TileServiceManager service) {
+ synchronized (mServices) {
+ service.setBindAllowed(false);
+ mServices.remove(tile);
+ mTiles.remove(tile.getComponent());
+ }
+ }
+
+ public void setMemoryPressure(boolean memoryPressure) {
+ mMaxBound = memoryPressure ? REDUCED_MAX_BOUND : DEFAULT_MAX_BOUND;
+ recalculateBindAllowance();
+ }
+
+ public void recalculateBindAllowance() {
+ final ArrayList<TileServiceManager> services;
+ synchronized (mServices) {
+ services = new ArrayList<>(mServices.values());
+ }
+ final int N = services.size();
+ if (N > mMaxBound) {
+ long currentTime = System.currentTimeMillis();
+ // Precalculate the priority of services for binding.
+ for (int i = 0; i < N; i++) {
+ services.get(i).calculateBindPriority(currentTime);
+ }
+ // Sort them so we can bind the most important first.
+ Collections.sort(services, SERVICE_SORT);
+ }
+ int i;
+ // Allow mMaxBound items to bind.
+ for (i = 0; i < mMaxBound && i < N; i++) {
+ services.get(i).setBindAllowed(true);
+ }
+ // The rest aren't allowed to bind for now.
+ while (i < N) {
+ services.get(i).setBindAllowed(false);
+ i++;
+ }
+ }
+
+ private void verifyCaller(String packageName) {
+ try {
+ int uid = mContext.getPackageManager().getPackageUid(packageName,
+ Binder.getCallingUserHandle().getIdentifier());
+ if (Binder.getCallingUid() != uid) {
+ throw new SecurityException("Component outside caller's uid");
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ throw new SecurityException(e);
+ }
+ }
+
+ @Override
+ public void updateQsTile(Tile tile) {
+ verifyCaller(tile.getComponentName().getPackageName());
+ CustomTile customTile = getTileForComponent(tile.getComponentName());
+ if (customTile != null) {
+ mServices.get(customTile).setLastUpdate(System.currentTimeMillis());
+ customTile.updateState(tile);
+ customTile.refreshState();
+ }
+ }
+
+ @Override
+ public void onShowDialog(Tile tile) {
+ verifyCaller(tile.getComponentName().getPackageName());
+ CustomTile customTile = getTileForComponent(tile.getComponentName());
+ if (customTile != null) {
+ customTile.onDialogShown();
+ mHost.collapsePanels();
+ }
+ }
+
+ private CustomTile getTileForComponent(ComponentName component) {
+ return mTiles.get(component);
+ }
+
+ private static final Comparator<TileServiceManager> SERVICE_SORT =
+ new Comparator<TileServiceManager>() {
+ @Override
+ public int compare(TileServiceManager left, TileServiceManager right) {
+ return -Integer.compare(left.getBindPriority(), right.getBindPriority());
+ }
+ };
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
index fc802dd..c696f88 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/AirplaneModeTile.java
@@ -58,8 +58,6 @@
public void handleClick() {
MetricsLogger.action(mContext, getMetricsCategory(), !mState.value);
setEnabled(!mState.value);
- mEnable.setAllowAnimation(true);
- mDisable.setAllowAnimation(true);
}
private void setEnabled(boolean enabled) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java
index f73ee35..23a15b9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/ColorInversionTile.java
@@ -71,8 +71,6 @@
protected void handleClick() {
MetricsLogger.action(mContext, getMetricsCategory(), !mState.value);
mSetting.setValue(mState.value ? 0 : 1);
- mEnable.setAllowAnimation(true);
- mDisable.setAllowAnimation(true);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
index d96f735..4f9f46d 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/DndTile.java
@@ -107,8 +107,6 @@
Toast.LENGTH_LONG).show();
return;
}
- mDisable.setAllowAnimation(true);
- mDisableTotalSilence.setAllowAnimation(true);
MetricsLogger.action(mContext, getMetricsCategory(), !mState.value);
if (mState.value) {
mController.setZen(Global.ZEN_MODE_OFF, null, TAG);
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
index 12c1298..39d9da1 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
@@ -64,7 +64,7 @@
}
MetricsLogger.action(mContext, getMetricsCategory(), !mState.value);
boolean newState = !mState.value;
- refreshState(newState ? UserBoolean.USER_TRUE : UserBoolean.USER_FALSE);
+ refreshState(newState);
mFlashlightController.setFlashlight(newState);
}
@@ -73,8 +73,8 @@
// TODO: Flashlight available handling...
// state.visible = mFlashlightController.isAvailable();
state.label = mHost.getContext().getString(R.string.quick_settings_flashlight_label);
- if (arg instanceof UserBoolean) {
- boolean value = ((UserBoolean) arg).value;
+ if (arg instanceof Boolean) {
+ boolean value = (Boolean) arg;
if (value == state.value) {
return;
}
@@ -83,7 +83,6 @@
state.value = mFlashlightController.isEnabled();
}
final AnimationIcon icon = state.value ? mEnable : mDisable;
- icon.setAllowAnimation(arg instanceof UserBoolean && ((UserBoolean) arg).userInitiated);
state.icon = icon;
int onOrOffId = state.value
? R.string.accessibility_quick_settings_flashlight_on
@@ -107,12 +106,12 @@
@Override
public void onFlashlightChanged(boolean enabled) {
- refreshState(enabled ? UserBoolean.BACKGROUND_TRUE : UserBoolean.BACKGROUND_FALSE);
+ refreshState(enabled);
}
@Override
public void onFlashlightError() {
- refreshState(UserBoolean.BACKGROUND_FALSE);
+ refreshState(false);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
index 250d567..55aa32b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/HotspotTile.java
@@ -59,8 +59,6 @@
final boolean isEnabled = (Boolean) mState.value;
MetricsLogger.action(mContext, getMetricsCategory(), !isEnabled);
mController.setHotspotEnabled(!isEnabled);
- mEnable.setAllowAnimation(true);
- mDisable.setAllowAnimation(true);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
index 08540f6..e79aabf 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/LocationTile.java
@@ -67,8 +67,6 @@
mHost.openPanels();
MetricsLogger.action(mContext, getMetricsCategory(), !wasEnabled);
mController.setLocationEnabled(!wasEnabled);
- mEnable.setAllowAnimation(true);
- mDisable.setAllowAnimation(true);
}
});
return;
@@ -76,8 +74,6 @@
final boolean wasEnabled = (Boolean) mState.value;
MetricsLogger.action(mContext, getMetricsCategory(), !wasEnabled);
mController.setLocationEnabled(!wasEnabled);
- mEnable.setAllowAnimation(true);
- mDisable.setAllowAnimation(true);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
index d85cf60..7bce54b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/RotationLockTile.java
@@ -63,15 +63,14 @@
MetricsLogger.action(mContext, getMetricsCategory(), !mState.value);
final boolean newState = !mState.value;
mController.setRotationLocked(newState);
- refreshState(newState ? UserBoolean.USER_TRUE : UserBoolean.USER_FALSE);
+ refreshState(newState);
}
@Override
protected void handleUpdateState(BooleanState state, Object arg) {
if (mController == null) return;
- final boolean rotationLocked = arg != null ? ((UserBoolean) arg).value
+ final boolean rotationLocked = arg != null ? (Boolean) arg
: mController.isRotationLocked();
- final boolean userInitiated = arg != null ? ((UserBoolean) arg).userInitiated : false;
// TODO: Handle accessibility rotation lock and whatnot.
// state.visible = mController.isRotationLockAffordanceVisible();
if (state.value == rotationLocked && state.contentDescription != null) {
@@ -80,18 +79,15 @@
}
state.value = rotationLocked;
final boolean portrait = isCurrentOrientationLockPortrait();
- final AnimationIcon icon;
if (rotationLocked) {
final int label = portrait ? R.string.quick_settings_rotation_locked_portrait_label
: R.string.quick_settings_rotation_locked_landscape_label;
state.label = mContext.getString(label);
- icon = portrait ? mAutoToPortrait : mAutoToLandscape;
+ state.icon = portrait ? mAutoToPortrait : mAutoToLandscape;
} else {
state.label = mContext.getString(R.string.quick_settings_rotation_unlocked_label);
- icon = portrait ? mPortraitToAuto : mLandscapeToAuto;
+ state.icon = portrait ? mPortraitToAuto : mLandscapeToAuto;
}
- icon.setAllowAnimation(userInitiated);
- state.icon = icon;
state.contentDescription = getAccessibilityString(rotationLocked,
R.string.accessibility_rotation_lock_on_portrait,
R.string.accessibility_rotation_lock_on_landscape,
@@ -145,8 +141,7 @@
private final RotationLockControllerCallback mCallback = new RotationLockControllerCallback() {
@Override
public void onRotationLockStateChanged(boolean rotationLocked, boolean affordanceVisible) {
- refreshState(rotationLocked ? UserBoolean.BACKGROUND_TRUE
- : UserBoolean.BACKGROUND_FALSE);
+ refreshState(rotationLocked);
}
};
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java
index 07915f8..255f29f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/WorkModeTile.java
@@ -93,12 +93,7 @@
}
private void refreshQuietModeState(boolean backgroundRefresh) {
- if (backgroundRefresh) {
- refreshState(isWorkModeEnabled() ? UserBoolean.BACKGROUND_TRUE
- : UserBoolean.BACKGROUND_FALSE);
- } else {
- refreshState(isWorkModeEnabled() ? UserBoolean.USER_TRUE : UserBoolean.USER_FALSE);
- }
+ refreshState(isWorkModeEnabled());
}
@Override
@@ -108,28 +103,22 @@
return;
}
- final boolean userInitialized;
- if (arg instanceof UserBoolean) {
- state.value = ((UserBoolean) arg).value;
- userInitialized = ((UserBoolean) arg).userInitiated;
+ if (arg instanceof Boolean) {
+ state.value = (Boolean) arg;
} else {
state.value = isWorkModeEnabled();
- userInitialized = false;
}
- final AnimationIcon icon;
state.label = mContext.getString(R.string.quick_settings_work_mode_label);
if (state.value) {
- icon = mEnable;
+ state.icon = mEnable;
state.contentDescription = mContext.getString(
R.string.accessibility_quick_settings_work_mode_on);
} else {
- icon = mDisable;
+ state.icon = mDisable;
state.contentDescription = mContext.getString(
R.string.accessibility_quick_settings_work_mode_off);
}
- icon.setAllowAnimation(userInitialized);
- state.icon = icon;
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
index 7547570f..cfbd1cb 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsConfiguration.java
@@ -74,9 +74,6 @@
public int svelteLevel;
public int searchBarSpaceHeightPx;
- /** Dev options and global settings */
- public boolean lockToAppEnabled;
-
public RecentsConfiguration(Context context) {
// Load only resources that can not change after the first load either through developer
// settings or via multi window
@@ -106,12 +103,7 @@
/**
* Updates the configuration based on the current state of the system
*/
- void update(Context context, SystemServicesProxy ssp, Rect windowRect) {
- // Only update resources that can change after the first load, either through developer
- // settings or via multi window
- lockToAppEnabled = !ssp.hasFreeformWorkspaceSupport() &&
- ssp.getSystemSetting(context, Settings.System.LOCK_TO_APP_ENABLED) != 0;
-
+ void update(Rect windowRect) {
// Recompute some values based on the given state, since we can not rely on the resource
// system to get certain values.
boolean isLandscape = windowRect.width() > windowRect.height();
diff --git a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
index 949fb86..2b988a9 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/RecentsImpl.java
@@ -572,7 +572,7 @@
Rect windowRect = ssp.getWindowRect();
// Update the configuration for the current state
- config.update(mContext, ssp, ssp.getWindowRect());
+ config.update(windowRect);
if (!RecentsDebugFlags.Static.DisableSearchBar && tryAndBindSearchWidget) {
// Try and pre-emptively bind the search widget on startup to ensure that we
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
index 7a92b2a..d6262ac 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
@@ -155,7 +155,7 @@
// Add the task to the stack
Task task = new Task(taskKey, t.affiliatedTaskId, t.affiliatedTaskColor, icon,
thumbnail, title, contentDescription, activityColor, !isStackTask,
- (i == (taskCount - 1)), config.lockToAppEnabled, t.bounds, t.taskDescription);
+ t.bounds, t.taskDescription);
allTasks.add(task);
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
index 34a0e52..d030fc1 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/Task.java
@@ -131,8 +131,6 @@
*/
public boolean isLaunchTarget;
public boolean isHistorical;
- public boolean lockToThisTask;
- public boolean lockToTaskEnabled;
private ArrayList<TaskCallbacks> mCallbacks = new ArrayList<>();
@@ -142,8 +140,8 @@
public Task(TaskKey key, int affiliationTaskId, int affiliationColor, Drawable icon,
Bitmap thumbnail, String title, String contentDescription, int colorPrimary,
- boolean isHistorical, boolean lockToThisTask, boolean lockToTaskEnabled,
- Rect bounds, ActivityManager.TaskDescription taskDescription) {
+ boolean isHistorical, Rect bounds,
+ ActivityManager.TaskDescription taskDescription) {
boolean isInAffiliationGroup = (affiliationTaskId != key.id);
boolean hasAffiliationGroupColor = isInAffiliationGroup && (affiliationColor != 0);
this.key = key;
@@ -159,8 +157,6 @@
this.bounds = bounds;
this.taskDescription = taskDescription;
this.isHistorical = isHistorical;
- this.lockToThisTask = lockToTaskEnabled && lockToThisTask;
- this.lockToTaskEnabled = lockToTaskEnabled;
}
/** Copies the other task. */
@@ -178,8 +174,6 @@
this.bounds = o.bounds;
this.isLaunchTarget = o.isLaunchTarget;
this.isHistorical = o.isHistorical;
- this.lockToThisTask = o.lockToThisTask;
- this.lockToTaskEnabled = o.lockToTaskEnabled;
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
index 6f003ab..5e720cb 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/TaskStack.java
@@ -427,8 +427,6 @@
removeGroup(group);
}
}
- // Update the lock-to-app state
- t.lockToThisTask = false;
}
/** Removes a task */
@@ -437,9 +435,6 @@
boolean wasFrontMostTask = (getStackFrontMostTask() == t);
removeTaskImpl(mStackTaskList, t);
Task newFrontMostTask = getStackFrontMostTask();
- if (newFrontMostTask != null && newFrontMostTask.lockToTaskEnabled) {
- newFrontMostTask.lockToThisTask = true;
- }
if (mCb != null) {
// Notify that a task has been removed
mCb.onStackTaskRemoved(this, t, wasFrontMostTask, newFrontMostTask);
@@ -524,10 +519,21 @@
mAffinitiesGroups.clear();
}
- /** Gets the front task */
+ /**
+ * Gets the front-most task in the stack.
+ */
public Task getStackFrontMostTask() {
- if (mStackTaskList.size() == 0) return null;
- return mStackTaskList.getTasks().get(mStackTaskList.size() - 1);
+ ArrayList<Task> stackTasks = mStackTaskList.getTasks();
+ if (stackTasks.isEmpty()) {
+ return null;
+ }
+ for (int i = stackTasks.size() - 1; i >= 0; i--) {
+ Task task = stackTasks.get(i);
+ if (!task.isFreeformTask()) {
+ return task;
+ }
+ }
+ return null;
}
/** Gets the task keys */
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
index 830d607..94fae13 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskStackView.java
@@ -28,6 +28,7 @@
import android.graphics.drawable.GradientDrawable;
import android.os.Bundle;
import android.os.Parcelable;
+import android.provider.Settings;
import android.util.IntProperty;
import android.util.Log;
import android.util.Property;
@@ -149,7 +150,9 @@
List<TaskView> mImmutableTaskViews = new ArrayList<>();
List<TaskView> mTmpTaskViews = new ArrayList<>();
LayoutInflater mInflater;
+
boolean mTouchExplorationEnabled;
+ boolean mScreenPinningEnabled;
Interpolator mFastOutSlowInInterpolator;
@@ -224,6 +227,8 @@
protected void onAttachedToWindow() {
SystemServicesProxy ssp = Recents.getSystemServices();
mTouchExplorationEnabled = ssp.isTouchExplorationEnabled();
+ mScreenPinningEnabled = ssp.getSystemSetting(getContext(),
+ Settings.System.LOCK_TO_APP_ENABLED) != 0;
EventBus.getDefault().register(this, RecentsActivity.EVENT_BUS_PRIORITY + 1);
super.onAttachedToWindow();
}
@@ -1023,8 +1028,7 @@
launchTargetTask);
hideTask = launchTargetTask.isFreeformTask() && task.isFreeformTask();
}
- tv.prepareEnterRecentsAnimation(task.isLaunchTarget, hideTask, occludesLaunchTarget,
- offscreenY);
+ tv.prepareEnterRecentsAnimation(hideTask, occludesLaunchTarget, offscreenY);
}
// If the enter animation started already and we haven't completed a layout yet, do the
@@ -1087,6 +1091,7 @@
ctx.currentTaskRect = mLayoutAlgorithm.mTaskRect;
ctx.currentTaskOccludesLaunchTarget = (launchTargetTask != null) &&
launchTargetTask.group.isTaskAboveTask(task, launchTargetTask);
+ ctx.isScreenPinningEnabled = mScreenPinningEnabled;
ctx.updateListener = mRequestUpdateClippingListener;
mLayoutAlgorithm.getStackTransform(task, mStackScroller.getStackScroll(),
ctx.currentTaskTransform, null);
@@ -1271,13 +1276,11 @@
requestSynchronizeStackViewsWithModel(DEFAULT_SYNC_STACK_DURATION);
}
- // Update the new front most task
- if (newFrontMostTask != null) {
+ // Update the new front most task's action button
+ if (mScreenPinningEnabled && newFrontMostTask != null) {
TaskView frontTv = getChildViewForTask(newFrontMostTask);
if (frontTv != null) {
- frontTv.onTaskBound(newFrontMostTask);
- frontTv.fadeInActionButton(getResources().getInteger(
- R.integer.recents_task_enter_from_app_duration));
+ frontTv.showActionButton(true /* fadeIn */, DEFAULT_SYNC_STACK_DURATION);
}
}
@@ -1304,7 +1307,7 @@
@Override
public void prepareViewToEnterPool(TaskView tv) {
- Task task = tv.getTask();
+ final Task task = tv.getTask();
// Report that this tasks's data is no longer being used
Recents.getTaskLoader().unloadTaskData(task);
@@ -1314,14 +1317,13 @@
// Update the task views list after removing the task view
updateTaskViewsList();
- // Reset the view properties
+ // Reset the view properties and view state
tv.resetViewProperties();
-
- // Reset the focused view state
tv.setFocusedState(false, false /* animated */, false /* requestViewFocus */);
-
- // Reset the clip state of the task view
tv.setClipViewInStack(false);
+ if (mScreenPinningEnabled) {
+ tv.hideActionButton();
+ }
}
@Override
@@ -1362,6 +1364,11 @@
if (mFocusedTask == task) {
tv.setFocusedState(true, false /* animated */, false /* requestViewFocus */);
}
+
+ // Restore the action button visibility if it is the front most task view
+ if (mScreenPinningEnabled && tv.getTask() == mStack.getStackFrontMostTask()) {
+ tv.showActionButton(false /* fadeIn */, 0 /* fadeInDuration */);
+ }
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
index a3e8b2d..14909c5 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskView.java
@@ -138,6 +138,7 @@
setBackground(new FakeShadowDrawable(res, config));
}
setOutlineProvider(mViewBounds);
+ setOnLongClickListener(this);
}
/** Set callback */
@@ -175,8 +176,10 @@
public void getOutline(View view, Outline outline) {
// Set the outline to match the FAB background
outline.setOval(0, 0, mActionButtonView.getWidth(), mActionButtonView.getHeight());
+ outline.setAlpha(0.35f);
}
});
+ mActionButtonView.setOnClickListener(this);
mActionButtonTranslationZ = mActionButtonView.getTranslationZ();
}
@@ -293,8 +296,8 @@
/** Prepares this task view for the enter-recents animations. This is called earlier in the
* first layout because the actual animation into recents may take a long time. */
- void prepareEnterRecentsAnimation(boolean isTaskViewLaunchTargetTask, boolean hideTask,
- boolean occludesLaunchTarget, int offscreenY) {
+ void prepareEnterRecentsAnimation(boolean hideTask, boolean occludesLaunchTarget,
+ int offscreenY) {
RecentsConfiguration config = Recents.getConfiguration();
RecentsActivityLaunchState launchState = config.getLaunchState();
int initialDim = getDim();
@@ -303,7 +306,7 @@
} else if (launchState.launchedHasConfigurationChanged) {
// Just load the views as-is
} else if (launchState.launchedFromAppWithThumbnail) {
- if (isTaskViewLaunchTargetTask) {
+ if (mTask.isLaunchTarget) {
// Set the dim to 0 so we can animate it in
initialDim = 0;
// Hide the action button
@@ -342,11 +345,15 @@
if (launchState.launchedFromAppWithThumbnail) {
if (mTask.isLaunchTarget) {
ctx.postAnimationTrigger.increment();
- // Immediately start the dim animation
+ // Start the dim animation
animateDimToProgress(taskViewEnterFromAppDuration,
ctx.postAnimationTrigger.decrementOnAnimationEnd());
- // Animate the action button in
- fadeInActionButton(taskViewEnterFromAppDuration);
+
+ // Start the action button animation
+ if (ctx.isScreenPinningEnabled) {
+ showActionButton(true /* fadeIn */,
+ taskViewEnterFromAppDuration /* fadeInDuration */);
+ }
} else {
// Animate the task up if it was occluding the launch target
if (ctx.currentTaskOccludesLaunchTarget) {
@@ -413,17 +420,6 @@
animate().cancel();
}
- public void fadeInActionButton(int duration) {
- // Hide the action button
- mActionButtonView.setAlpha(0f);
-
- // Animate the action button in
- mActionButtonView.animate().alpha(1f)
- .setDuration(duration)
- .setInterpolator(PhoneStatusBar.ALPHA_IN)
- .start();
- }
-
/** Animates this task view as it leaves recents by pressing home. */
void startExitToHomeAnimation(ViewAnimation.TaskViewExitContext ctx) {
int taskViewExitToHomeDuration = getResources().getInteger(
@@ -468,8 +464,9 @@
.alpha(0f)
.setStartDelay(0)
.setDuration(taskViewExitToAppDuration)
- .setInterpolator(mFastOutLinearInInterpolator)
+ .setInterpolator(PhoneStatusBar.ALPHA_OUT)
.withEndAction(postAnimRunnable)
+ .withLayer()
.start();
} else {
// Hide the dismiss button
@@ -644,8 +641,6 @@
setDim(getDimFromTaskProgress());
}
- /**** View focus state ****/
-
/**
* Explicitly sets the focused state of this task.
*/
@@ -672,19 +667,42 @@
}
}
+ /**
+ * Shows the action button.
+ * @param fadeIn whether or not to animate the action button in.
+ * @param fadeInDuration the duration of the action button animation, only used if
+ * {@param fadeIn} is true.
+ */
+ public void showActionButton(boolean fadeIn, int fadeInDuration) {
+ mActionButtonView.setVisibility(View.VISIBLE);
+
+ if (fadeIn) {
+ if (mActionButtonView.getAlpha() < 1f) {
+ mActionButtonView.setAlpha(0f);
+ mActionButtonView.animate().alpha(1f)
+ .setDuration(fadeInDuration)
+ .setInterpolator(PhoneStatusBar.ALPHA_IN)
+ .withLayer()
+ .start();
+ }
+ } else {
+ mActionButtonView.setAlpha(1f);
+ }
+ }
+
+ /**
+ * Immediately hides the action button.
+ */
+ public void hideActionButton() {
+ mActionButtonView.setVisibility(View.INVISIBLE);
+ }
+
/**** TaskCallbacks Implementation ****/
/** Binds this task view to the task */
public void onTaskBound(Task t) {
mTask = t;
mTask.addCallback(this);
-
- // Hide the action button if lock to app is disabled for this view
- int lockButtonVisibility = (!t.lockToTaskEnabled || !t.lockToThisTask) ? GONE : VISIBLE;
- if (mActionButtonView.getVisibility() != lockButtonVisibility) {
- mActionButtonView.setVisibility(lockButtonVisibility);
- requestLayout();
- }
}
@Override
@@ -693,10 +711,6 @@
// Bind each of the views to the new task data
mThumbnailView.rebindToTask(mTask);
mHeaderView.rebindToTask(mTask);
-
- // Rebind any listeners
- mActionButtonView.setOnClickListener(this);
- setOnLongClickListener(this);
}
mTaskDataLoaded = true;
}
@@ -708,8 +722,6 @@
mTask.removeCallback(this);
mThumbnailView.unbindFromTask();
mHeaderView.unbindFromTask();
- // Unbind any listeners
- mActionButtonView.setOnClickListener(null);
}
mTaskDataLoaded = false;
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/ViewAnimation.java b/packages/SystemUI/src/com/android/systemui/recents/views/ViewAnimation.java
index e1d80fd..eaef51c 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/ViewAnimation.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/ViewAnimation.java
@@ -33,6 +33,8 @@
// These following properties are updated for each task view we start the enter animation on
+ // Whether or not screen pinning is enabled
+ boolean isScreenPinningEnabled;
// Whether or not the current task occludes the launch target
boolean currentTaskOccludesLaunchTarget;
// The task rect for the current stack
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
index e6d837a..90a688f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/QSTileHost.java
@@ -18,30 +18,25 @@
import android.app.ActivityManager;
import android.app.PendingIntent;
-import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
-import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Resources;
-import android.os.Binder;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Process;
-import android.os.RemoteException;
import android.provider.Settings;
-import android.service.quicksettings.IQSService;
-import android.service.quicksettings.Tile;
import android.text.TextUtils;
import android.util.Log;
import com.android.systemui.R;
import com.android.systemui.qs.QSTile;
+import com.android.systemui.qs.external.CustomTile;
+import com.android.systemui.qs.external.TileServices;
import com.android.systemui.qs.tiles.AirplaneModeTile;
import com.android.systemui.qs.tiles.BatteryTile;
import com.android.systemui.qs.tiles.BluetoothTile;
import com.android.systemui.qs.tiles.CastTile;
import com.android.systemui.qs.tiles.CellularTile;
import com.android.systemui.qs.tiles.ColorInversionTile;
-import com.android.systemui.qs.tiles.CustomTile;
import com.android.systemui.qs.tiles.DndTile;
import com.android.systemui.qs.tiles.FlashlightTile;
import com.android.systemui.qs.tiles.HotspotTile;
@@ -76,7 +71,7 @@
import java.util.Map;
/** Platform implementation of the quick settings tile host **/
-public final class QSTileHost extends IQSService.Stub implements QSTile.Host, Tunable {
+public final class QSTileHost implements QSTile.Host, Tunable {
private static final String TAG = "QSTileHost";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
@@ -100,6 +95,7 @@
private final KeyguardMonitor mKeyguard;
private final SecurityController mSecurity;
private final BatteryController mBattery;
+ private final TileServices mServices;
private final List<Callback> mCallbacks = new ArrayList<>();
@@ -131,6 +127,8 @@
ht.start();
mLooper = ht.getLooper();
+ mServices = new TileServices(this, mLooper);
+
TunerService.get(mContext).addTunable(this, TILES_SETTING);
}
@@ -256,6 +254,10 @@
return mSecurity;
}
+ public TileServices getTileServices() {
+ return mServices;
+ }
+
@Override
public void onTuningChanged(String key, String newValue) {
if (!TILES_SETTING.equals(key)) {
@@ -306,50 +308,6 @@
TextUtils.join(",", specs), ActivityManager.getCurrentUser());
}
- @Override
- public void updateQsTile(Tile tile) throws RemoteException {
- verifyCaller(tile.getComponentName().getPackageName());
- CustomTile customTile = getTileForComponent(tile.getComponentName());
- if (customTile != null) {
- customTile.updateState(tile);
- customTile.refreshState();
- }
- }
-
- @Override
- public void onShowDialog(Tile tile) throws RemoteException {
- verifyCaller(tile.getComponentName().getPackageName());
- CustomTile customTile = getTileForComponent(tile.getComponentName());
- if (customTile != null) {
- customTile.onDialogShown();
- collapsePanels();
- }
- }
-
- private void verifyCaller(String packageName) {
- try {
- int uid = mContext.getPackageManager().getPackageUid(packageName,
- Binder.getCallingUserHandle().getIdentifier());
- if (Binder.getCallingUid() != uid) {
- throw new SecurityException("Component outside caller's uid");
- }
- } catch (NameNotFoundException e) {
- throw new SecurityException(e);
- }
- }
-
- private CustomTile getTileForComponent(ComponentName component) {
- // TODO: Build map for easier lookup.
- for (QSTile<?> qsTile : mTiles.values()) {
- if (qsTile instanceof CustomTile) {
- if (((CustomTile) qsTile).getComponent().equals(component)) {
- return (CustomTile) qsTile;
- }
- }
- }
- return null;
- }
-
public QSTile<?> createTile(String tileSpec) {
if (tileSpec.equals("wifi")) return WifiTile.isSupported(this)
? new WifiTile(this) : null;
diff --git a/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java b/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java
index 1e3b0f1..9081af1 100644
--- a/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java
+++ b/packages/SystemUI/src/com/android/systemui/tuner/TunerService.java
@@ -153,8 +153,11 @@
private static TunerService sInstance;
public static TunerService get(Context context) {
- SystemUIApplication sysUi = (SystemUIApplication) context.getApplicationContext();
- TunerService service = sysUi.getComponent(TunerService.class);
+ TunerService service = null;
+ if (context.getApplicationContext() instanceof SystemUIApplication) {
+ SystemUIApplication sysUi = (SystemUIApplication) context.getApplicationContext();
+ service = sysUi.getComponent(TunerService.class);
+ }
if (service == null) {
// Can't get it as a component, must in the tuner, lets just create one for now.
return getStaticService(context);
diff --git a/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java b/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
index cd47ac1..38d8de0 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/ZenModePanel.java
@@ -510,7 +510,7 @@
GregorianCalendar weekRange = new GregorianCalendar();
final long now = weekRange.getTimeInMillis();
setToMidnight(weekRange);
- weekRange.roll(Calendar.DATE, 6);
+ weekRange.add(Calendar.DATE, 6);
final long nextAlarmMs = mController.getNextAlarm();
if (nextAlarmMs > 0) {
GregorianCalendar nextAlarm = new GregorianCalendar();
diff --git a/packages/SystemUI/tests/AndroidManifest.xml b/packages/SystemUI/tests/AndroidManifest.xml
index c21af24..2825601 100644
--- a/packages/SystemUI/tests/AndroidManifest.xml
+++ b/packages/SystemUI/tests/AndroidManifest.xml
@@ -19,10 +19,16 @@
<uses-permission android:name="android.permission.INJECT_EVENTS" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
+ <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" />
+ <uses-permission android:name="android.permission.MANAGE_USERS" />
<application>
<uses-library android:name="android.test.runner" />
<activity android:name="com.android.systemui.screenshot.ScreenshotStubActivity" />
+
+ <service
+ android:name="com.android.systemui.qs.external.TileLifecycleManagerTests$FakeTileService"
+ android:process=":killable" />
</application>
<instrumentation android:name="android.test.InstrumentationTestRunner"
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileLifecycleManagerTests.java b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileLifecycleManagerTests.java
new file mode 100644
index 0000000..0594211
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileLifecycleManagerTests.java
@@ -0,0 +1,298 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.qs.external;
+
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Process;
+import android.os.UserHandle;
+import android.service.quicksettings.TileService;
+import android.test.AndroidTestCase;
+import android.util.ArraySet;
+import android.util.Log;
+
+public class TileLifecycleManagerTests extends AndroidTestCase {
+ public static final String TILE_UPDATE_BROADCAST = "com.android.systemui.tests.TILE_UPDATE";
+ public static final String EXTRA_CALLBACK = "callback";
+
+ private HandlerThread mThread;
+ private Handler mHandler;
+ private TileLifecycleManager mStateManager;
+ private final Object mBroadcastLock = new Object();
+ private final ArraySet<String> mCallbacks = new ArraySet<>();
+ private boolean mBound;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mThread = new HandlerThread("TestThread");
+ mThread.start();
+ mHandler = new Handler(mThread.getLooper());
+ mStateManager = new TileLifecycleManager(mHandler, getContext(),
+ new Intent(mContext, FakeTileService.class), new UserHandle(UserHandle.myUserId()));
+ mCallbacks.clear();
+ getContext().registerReceiver(mReceiver, new IntentFilter(TILE_UPDATE_BROADCAST));
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ super.tearDown();
+ if (mBound) {
+ unbindService();
+ }
+ mThread.quit();
+ getContext().unregisterReceiver(mReceiver);
+ }
+
+ public void testSync() {
+ syncWithHandler();
+ }
+
+ public void testBind() {
+ bindService();
+ waitForCallback("onCreate");
+ }
+
+ public void testUnbind() {
+ bindService();
+ waitForCallback("onCreate");
+ unbindService();
+ waitForCallback("onDestroy");
+ }
+
+ public void testTileServiceCallbacks() {
+ bindService();
+ waitForCallback("onCreate");
+
+ mStateManager.onTileAdded();
+ waitForCallback("onTileAdded");
+ mStateManager.onStartListening();
+ waitForCallback("onStartListening");
+ mStateManager.onClick(null);
+ waitForCallback("onClick");
+ mStateManager.onStopListening();
+ waitForCallback("onStopListening");
+ mStateManager.onTileRemoved();
+ waitForCallback("onTileRemoved");
+
+ unbindService();
+ }
+
+ public void testAddedBeforeBind() {
+ mStateManager.onTileAdded();
+
+ bindService();
+ waitForCallback("onCreate");
+ waitForCallback("onTileAdded");
+ }
+
+ public void testListeningBeforeBind() {
+ mStateManager.onTileAdded();
+ mStateManager.onStartListening();
+
+ bindService();
+ waitForCallback("onCreate");
+ waitForCallback("onTileAdded");
+ waitForCallback("onStartListening");
+ }
+
+ public void testClickBeforeBind() {
+ mStateManager.onTileAdded();
+ mStateManager.onStartListening();
+ mStateManager.onClick(null);
+
+ bindService();
+ waitForCallback("onCreate");
+ waitForCallback("onTileAdded");
+ waitForCallback("onStartListening");
+ waitForCallback("onClick");
+ }
+
+ public void testListeningNotListeningBeforeBind() {
+ mStateManager.onTileAdded();
+ mStateManager.onStartListening();
+ mStateManager.onStopListening();
+
+ bindService();
+ waitForCallback("onCreate");
+ unbindService();
+ waitForCallback("onDestroy");
+ assertFalse(mCallbacks.contains("onStartListening"));
+ }
+
+ public void testNoClickOfNotListeningAnymore() {
+ mStateManager.onTileAdded();
+ mStateManager.onStartListening();
+ mStateManager.onClick(null);
+ mStateManager.onStopListening();
+
+ bindService();
+ waitForCallback("onCreate");
+ unbindService();
+ waitForCallback("onDestroy");
+ assertFalse(mCallbacks.contains("onClick"));
+ }
+
+ public void testComponentEnabling() {
+ mStateManager.onTileAdded();
+ mStateManager.onStartListening();
+
+ PackageManager pm = getContext().getPackageManager();
+ pm.setComponentEnabledSetting(new ComponentName(getContext(), FakeTileService.class),
+ PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP);
+
+ bindService();
+ assertTrue(mStateManager.mReceiverRegistered);
+
+ pm.setComponentEnabledSetting(new ComponentName(getContext(), FakeTileService.class),
+ PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP);
+ waitForCallback("onCreate");
+ }
+
+ public void testKillProcess() {
+ mStateManager.onStartListening();
+ bindService();
+ waitForCallback("onCreate");
+ waitForCallback("onStartListening");
+
+ getContext().sendBroadcast(new Intent(FakeTileService.ACTION_KILL));
+
+ waitForCallback("onCreate");
+ waitForCallback("onStartListening");
+ }
+
+ private void bindService() {
+ mBound = true;
+ mStateManager.setBindService(true);
+ }
+
+ private void unbindService() {
+ mBound = false;
+ mStateManager.setBindService(false);
+ }
+
+ private void waitForCallback(String callback) {
+ for (int i = 0; i < 25; i++) {
+ if (mCallbacks.contains(callback)) {
+ mCallbacks.remove(callback);
+ return;
+ }
+ synchronized (mBroadcastLock) {
+ try {
+ mBroadcastLock.wait(500);
+ } catch (InterruptedException e) {
+ }
+ }
+ }
+ if (mCallbacks.contains(callback)) {
+ mCallbacks.remove(callback);
+ return;
+ }
+ fail("Didn't receive callback: " + callback);
+ }
+
+ private void syncWithHandler() {
+ final Object lock = new Object();
+ synchronized (lock) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ synchronized (lock) {
+ lock.notify();
+ }
+ }
+ });
+ try {
+ lock.wait(5000);
+ } catch (InterruptedException e) {
+ }
+ }
+ }
+
+ private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ mCallbacks.add(intent.getStringExtra(EXTRA_CALLBACK));
+ synchronized (mBroadcastLock) {
+ mBroadcastLock.notify();
+ }
+ }
+ };
+
+ public static class FakeTileService extends TileService {
+ public static final String ACTION_KILL = "com.android.systemui.test.KILL";
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ registerReceiver(mReceiver, new IntentFilter(ACTION_KILL));
+ sendCallback("onCreate");
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ unregisterReceiver(mReceiver);
+ sendCallback("onDestroy");
+ }
+
+ @Override
+ public void onTileAdded() {
+ sendCallback("onTileAdded");
+ }
+
+ @Override
+ public void onTileRemoved() {
+ sendCallback("onTileRemoved");
+ }
+
+ @Override
+ public void onStartListening() {
+ sendCallback("onStartListening");
+ }
+
+ @Override
+ public void onStopListening() {
+ sendCallback("onStopListening");
+ }
+
+ @Override
+ public void onClick() {
+ sendCallback("onClick");
+ }
+
+ private void sendCallback(String callback) {
+ Log.d("TileLifecycleManager", "Relaying: " + callback);
+ sendBroadcast(new Intent(TILE_UPDATE_BROADCAST)
+ .putExtra(EXTRA_CALLBACK, callback));
+ }
+
+ private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (ACTION_KILL.equals(intent.getAction())) {
+ Process.killProcess(Process.myPid());
+ }
+ }
+ };
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServiceManagerTests.java b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServiceManagerTests.java
new file mode 100644
index 0000000..c4f686e
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServiceManagerTests.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+package com.android.systemui.qs.external;
+
+import android.os.Handler;
+import android.os.HandlerThread;
+import com.android.systemui.SysuiTestCase;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mockito;
+
+public class TileServiceManagerTests extends SysuiTestCase {
+
+ private TileServices mTileServices;
+ private TileLifecycleManager mTileLifecycle;
+ private HandlerThread mThread;
+ private Handler mHandler;
+ private TileServiceManager mTileServiceManager;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mThread = new HandlerThread("TestThread");
+ mThread.start();
+ mHandler = new Handler(mThread.getLooper());
+ mTileServices = Mockito.mock(TileServices.class);
+ mTileLifecycle = Mockito.mock(TileLifecycleManager.class);
+ mTileServiceManager = new TileServiceManager(mTileServices, mHandler, mTileLifecycle);
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ super.tearDown();
+ mThread.quit();
+ }
+
+ public void testSetBindRequested() {
+ // Request binding.
+ mTileServiceManager.setBindRequested(true);
+ mTileServiceManager.setLastUpdate(0);
+ mTileServiceManager.calculateBindPriority(5);
+ Mockito.verify(mTileServices, Mockito.times(2)).recalculateBindAllowance();
+ assertEquals(5, mTileServiceManager.getBindPriority());
+
+ // Verify same state doesn't trigger recalculating for no reason.
+ mTileServiceManager.setBindRequested(true);
+ Mockito.verify(mTileServices, Mockito.times(2)).recalculateBindAllowance();
+
+ mTileServiceManager.setBindRequested(false);
+ mTileServiceManager.calculateBindPriority(5);
+ Mockito.verify(mTileServices, Mockito.times(3)).recalculateBindAllowance();
+ assertEquals(Integer.MIN_VALUE, mTileServiceManager.getBindPriority());
+ }
+
+ public void testPendingClickPriority() {
+ Mockito.when(mTileLifecycle.hasPendingClick()).thenReturn(true);
+ mTileServiceManager.calculateBindPriority(0);
+ assertEquals(Integer.MAX_VALUE, mTileServiceManager.getBindPriority());
+ }
+
+ public void testBind() {
+ // Trigger binding requested and allowed.
+ mTileServiceManager.setBindRequested(true);
+ mTileServiceManager.setBindAllowed(true);
+
+ ArgumentCaptor<Boolean> captor = ArgumentCaptor.forClass(Boolean.class);
+ Mockito.verify(mTileLifecycle, Mockito.times(1)).setBindService(captor.capture());
+ assertTrue((boolean) captor.getValue());
+
+ mTileServiceManager.setBindRequested(false);
+ mTileServiceManager.calculateBindPriority(0);
+ // Priority shouldn't disappear after the request goes away if we just bound, instead
+ // it sticks around to avoid thrashing a bunch of processes.
+ assertEquals(Integer.MAX_VALUE - 1, mTileServiceManager.getBindPriority());
+
+ mTileServiceManager.setBindAllowed(false);
+ captor = ArgumentCaptor.forClass(Boolean.class);
+ Mockito.verify(mTileLifecycle, Mockito.times(2)).setBindService(captor.capture());
+ assertFalse((boolean) captor.getValue());
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTests.java b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTests.java
new file mode 100644
index 0000000..7a3ce87
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTests.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+package com.android.systemui.qs.external;
+
+import android.content.ComponentName;
+import android.os.Looper;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.phone.QSTileHost;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mockito;
+
+import java.util.ArrayList;
+
+public class TileServicesTests extends SysuiTestCase {
+ private static int NUM_FAKES = TileServices.DEFAULT_MAX_BOUND * 2;
+
+ private TileServices mTileService;
+ private ArrayList<TileServiceManager> mManagers;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mManagers = new ArrayList<>();
+ QSTileHost host = new QSTileHost(mContext, null, null, null, null, null, null, null, null,
+ null, null, null, null, null, null);
+ mTileService = new TestTileServices(host, Looper.myLooper());
+ }
+
+ public void testRecalculateBindAllowance() {
+ // Add some fake tiles.
+ for (int i = 0; i < NUM_FAKES; i++) {
+ mTileService.getTileWrapper(Mockito.mock(CustomTile.class));
+ }
+ assertEquals(NUM_FAKES, mManagers.size());
+
+ for (int i = 0; i < NUM_FAKES; i++) {
+ Mockito.when(mManagers.get(i).getBindPriority()).thenReturn(i);
+ }
+ mTileService.recalculateBindAllowance();
+ for (int i = 0; i < NUM_FAKES; i++) {
+ Mockito.verify(mManagers.get(i), Mockito.times(1)).calculateBindPriority(
+ Mockito.anyLong());
+ ArgumentCaptor<Boolean> captor = ArgumentCaptor.forClass(Boolean.class);
+ Mockito.verify(mManagers.get(i), Mockito.times(1)).setBindAllowed(captor.capture());
+
+ assertEquals("" + i + "th service", i >= (NUM_FAKES - TileServices.DEFAULT_MAX_BOUND),
+ (boolean) captor.getValue());
+ }
+ }
+
+ public void testSetMemoryPressure() {
+ testRecalculateBindAllowance();
+ mTileService.setMemoryPressure(true);
+
+ for (int i = 0; i < NUM_FAKES; i++) {
+ ArgumentCaptor<Boolean> captor = ArgumentCaptor.forClass(Boolean.class);
+ Mockito.verify(mManagers.get(i), Mockito.times(2)).setBindAllowed(captor.capture());
+
+ assertEquals("" + i + "th service", i >= (NUM_FAKES - TileServices.REDUCED_MAX_BOUND),
+ (boolean) captor.getValue());
+ }
+ }
+
+ public void testCalcFew() {
+ for (int i = 0; i < TileServices.DEFAULT_MAX_BOUND - 1; i++) {
+ mTileService.getTileWrapper(Mockito.mock(CustomTile.class));
+ }
+ mTileService.recalculateBindAllowance();
+
+ for (int i = 0; i < TileServices.DEFAULT_MAX_BOUND - 1; i++) {
+ // Shouldn't get bind prioirities calculated when there are less than the max services.
+ Mockito.verify(mManagers.get(i), Mockito.never()).calculateBindPriority(
+ Mockito.anyLong());
+
+ // All should be bound since there are less than the max services.
+ ArgumentCaptor<Boolean> captor = ArgumentCaptor.forClass(Boolean.class);
+ Mockito.verify(mManagers.get(i), Mockito.times(1)).setBindAllowed(captor.capture());
+
+ assertTrue(captor.getValue());
+ }
+ }
+
+ private class TestTileServices extends TileServices {
+ public TestTileServices(QSTileHost host, Looper looper) {
+ super(host, looper);
+ }
+
+ @Override
+ protected TileServiceManager onCreateTileService(ComponentName component) {
+ TileServiceManager manager = Mockito.mock(TileServiceManager.class);
+ mManagers.add(manager);
+ return manager;
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 3e8dc4e..8e64eeb 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -2743,9 +2743,9 @@
return mAppBindArgs;
}
- final void setFocusedActivityLocked(ActivityRecord r, String reason) {
+ boolean setFocusedActivityLocked(ActivityRecord r, String reason) {
if (r == null || mFocusedActivity == r) {
- return;
+ return false;
}
if (DEBUG_FOCUS) Slog.d(TAG_FOCUS, "setFocusedActivityLocked: r=" + r);
@@ -2783,7 +2783,7 @@
finishVoiceTask(last.task.voiceSession);
}
}
- if (mStackSupervisor.setFocusedStack(r, reason + " setFocusedActivity")) {
+ if (mStackSupervisor.moveActivityStackToFront(r, reason + " setFocusedActivity")) {
mWindowManager.setFocusedApp(r.appToken, true);
}
applyUpdateLockStateLocked(r);
@@ -2799,23 +2799,35 @@
mFocusedActivity == null ? -1 : mFocusedActivity.userId,
mFocusedActivity == null ? "NULL" : mFocusedActivity.shortComponentName,
reason);
+ return true;
}
- final void clearFocusedActivity(ActivityRecord r) {
- if (mFocusedActivity == r) {
- ActivityStack stack = mStackSupervisor.getFocusedStack();
- if (stack != null) {
- ActivityRecord top = stack.topActivity();
- if (top != null && top.userId != mLastFocusedUserId) {
- mHandler.removeMessages(FOREGROUND_PROFILE_CHANGED_MSG);
- mHandler.sendMessage(mHandler.obtainMessage(FOREGROUND_PROFILE_CHANGED_MSG,
- top.userId, 0));
- mLastFocusedUserId = top.userId;
- }
- }
- mFocusedActivity = null;
- EventLogTags.writeAmFocusedActivity(-1, "NULL", "clearFocusedActivity");
+ final void resetFocusedActivityIfNeededLocked(ActivityRecord goingAway) {
+ if (mFocusedActivity != goingAway) {
+ return;
}
+
+ final ActivityStack focusedStack = mStackSupervisor.getFocusedStack();
+ if (focusedStack != null) {
+ final ActivityRecord top = focusedStack.topActivity();
+ if (top != null && top.userId != mLastFocusedUserId) {
+ mHandler.removeMessages(FOREGROUND_PROFILE_CHANGED_MSG);
+ mHandler.sendMessage(
+ mHandler.obtainMessage(FOREGROUND_PROFILE_CHANGED_MSG, top.userId, 0));
+ mLastFocusedUserId = top.userId;
+ }
+ }
+
+ // Try to move focus to another activity if possible.
+ if (setFocusedActivityLocked(
+ focusedStack.topRunningActivityLocked(), "resetFocusedActivityIfNeeded")) {
+ return;
+ }
+
+ if (DEBUG_FOCUS) Slog.d(TAG_FOCUS, "resetFocusedActivityIfNeeded: Setting focus to NULL "
+ + "prev mFocusedActivity=" + mFocusedActivity + " goingAway=" + goingAway);
+ mFocusedActivity = null;
+ EventLogTags.writeAmFocusedActivity(-1, "NULL", "resetFocusedActivityIfNeeded");
}
@Override
@@ -2827,7 +2839,7 @@
ActivityRecord r = stack.topRunningActivityLocked();
if (r != null) {
setFocusedActivityLocked(r, "setFocusedStack");
- mStackSupervisor.resumeTopActivitiesLocked(stack, null, null);
+ mStackSupervisor.resumeFocusedStackTopActivityLocked();
}
}
}
@@ -2844,7 +2856,7 @@
ActivityRecord r = task.topRunningActivityLocked();
if (r != null) {
setFocusedActivityLocked(r, "setFocusedTask");
- mStackSupervisor.resumeTopActivitiesLocked(task.stack, null, null);
+ mStackSupervisor.resumeFocusedStackTopActivityLocked();
}
}
}
@@ -4473,7 +4485,7 @@
if (config != null) {
r.frozenBeforeDestroy = true;
if (!updateConfigurationLocked(config, r, false)) {
- mStackSupervisor.resumeTopActivitiesLocked();
+ mStackSupervisor.resumeFocusedStackTopActivityLocked();
}
}
Binder.restoreCallingIdentity(origId);
@@ -4811,12 +4823,11 @@
finishInstrumentationLocked(app, Activity.RESULT_CANCELED, info);
}
- if (!restarting && hasVisibleActivities && !mStackSupervisor.resumeTopActivitiesLocked()) {
- // If there was nothing to resume, and we are not already
- // restarting this process, but there is a visible activity that
- // is hosted by the process... then make sure all visible
- // activities are running, taking care of restarting this
- // process.
+ if (!restarting && hasVisibleActivities
+ && !mStackSupervisor.resumeFocusedStackTopActivityLocked()) {
+ // If there was nothing to resume, and we are not already restarting this process, but
+ // there is a visible activity that is hosted by the process... then make sure all
+ // visible activities are running, taking care of restarting this process.
mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
}
}
@@ -5873,7 +5884,7 @@
// Clean-up disabled activities.
if (mStackSupervisor.finishDisabledPackageActivitiesLocked(
packageName, disabledClasses, true, false, userId) && mBooted) {
- mStackSupervisor.resumeTopActivitiesLocked();
+ mStackSupervisor.resumeFocusedStackTopActivityLocked();
mStackSupervisor.scheduleIdleLocked();
}
@@ -6061,7 +6072,7 @@
}
}
if (mBooted) {
- mStackSupervisor.resumeTopActivitiesLocked();
+ mStackSupervisor.resumeFocusedStackTopActivityLocked();
mStackSupervisor.scheduleIdleLocked();
}
}
@@ -8994,7 +9005,7 @@
task.mResizeable = resizeable;
mWindowManager.setTaskResizeable(taskId, resizeable);
mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
- mStackSupervisor.resumeTopActivitiesLocked();
+ mStackSupervisor.resumeFocusedStackTopActivityLocked();
}
}
}
@@ -12603,7 +12614,7 @@
} finally {
Binder.restoreCallingIdentity(ident);
}
- mStackSupervisor.resumeTopActivitiesLocked();
+ mStackSupervisor.resumeFocusedStackTopActivityLocked();
mUserController.sendUserSwitchBroadcastsLocked(-1, currentUserId);
}
}
@@ -12714,10 +12725,10 @@
// annoy the user repeatedly. Unless it is persistent, since those
// processes run critical code.
removeProcessLocked(app, false, false, "crash");
- mStackSupervisor.resumeTopActivitiesLocked();
+ mStackSupervisor.resumeFocusedStackTopActivityLocked();
return false;
}
- mStackSupervisor.resumeTopActivitiesLocked();
+ mStackSupervisor.resumeFocusedStackTopActivityLocked();
} else {
mStackSupervisor.finishTopRunningActivityLocked(app, reason);
}
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index 1492e23..b9ff32c 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -572,6 +572,10 @@
}
}
+ boolean isFocusable() {
+ return StackId.canReceiveKeys(mStackId);
+ }
+
final boolean isAttached() {
return mStacks != null;
}
@@ -917,7 +921,7 @@
if (prev == null) {
if (!resuming) {
Slog.wtf(TAG, "Trying to pause when nothing is resumed");
- mStackSupervisor.resumeTopActivitiesLocked();
+ mStackSupervisor.resumeFocusedStackTopActivityLocked();
}
return false;
}
@@ -1008,7 +1012,7 @@
// pause, so just treat it as being paused now.
if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Activity not running, resuming next.");
if (!resuming) {
- mStackSupervisor.getFocusedStack().resumeTopActivityLocked(null);
+ mStackSupervisor.resumeFocusedStackTopActivityLocked();
}
return false;
}
@@ -1075,7 +1079,7 @@
} else {
if (r.configDestroy) {
destroyActivityLocked(r, true, "stop-config");
- mStackSupervisor.resumeTopActivitiesLocked();
+ mStackSupervisor.resumeFocusedStackTopActivityLocked();
} else {
mStackSupervisor.updatePreviousProcessLocked(r);
}
@@ -1131,17 +1135,16 @@
if (resumeNext) {
final ActivityStack topStack = mStackSupervisor.getFocusedStack();
if (!mService.isSleepingOrShuttingDown()) {
- mStackSupervisor.resumeTopActivitiesLocked(topStack, prev, null);
+ mStackSupervisor.resumeFocusedStackTopActivityLocked(topStack, prev, null);
} else {
mStackSupervisor.checkReadyForSleepLocked();
ActivityRecord top = topStack.topRunningActivityLocked();
if (top == null || (prev != null && top != prev)) {
- // If there are no more activities available to run,
- // do resume anyway to start something. Also if the top
- // activity on the stack is not the just paused activity,
- // we need to go ahead and resume it to ensure we complete
- // an in-flight app switch.
- mStackSupervisor.resumeTopActivitiesLocked(topStack, null, null);
+ // If there are no more activities available to run, do resume anyway to start
+ // something. Also if the top activity on the stack is not the just paused
+ // activity, we need to go ahead and resume it to ensure we complete an
+ // in-flight app switch.
+ mStackSupervisor.resumeFocusedStackTopActivityLocked();
}
}
}
@@ -1478,7 +1481,7 @@
boolean aboveTop = top != null;
final boolean stackInvisible = !isStackVisibleLocked();
boolean behindFullscreenActivity = stackInvisible;
- boolean noStackActivityResumed = (isInStackLocked(starting) == null);
+ boolean resumeNextActivity = isFocusable() && (isInStackLocked(starting) == null);
for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
final TaskRecord task = mTaskHistory.get(taskNdx);
@@ -1509,18 +1512,18 @@
if (r.app == null || r.app.thread == null) {
if (makeVisibleAndRestartIfNeeded(starting, configChanges, isTop,
- noStackActivityResumed, r)) {
+ resumeNextActivity, r)) {
if (activityNdx >= activities.size()) {
// Record may be removed if its process needs to restart.
activityNdx = activities.size() - 1;
} else {
- noStackActivityResumed = false;
+ resumeNextActivity = false;
}
}
} else if (r.visible) {
// If this activity is already visible, then there is nothing to do here.
if (handleAlreadyVisible(r)) {
- noStackActivityResumed = false;
+ resumeNextActivity = false;
}
} else {
makeVisible(starting, r);
@@ -1561,7 +1564,7 @@
}
private boolean makeVisibleAndRestartIfNeeded(ActivityRecord starting, int configChanges,
- boolean isTop, boolean noStackActivityResumed, ActivityRecord r) {
+ boolean isTop, boolean andResume, ActivityRecord r) {
// We need to make sure the app is running if it's the top, or it is just made visible from
// invisible. If the app is already visible, it must have died while it was visible. In this
// case, we'll show the dead window but will not restart the app. Otherwise we could end up
@@ -1578,7 +1581,7 @@
setVisible(r, true);
}
if (r != starting) {
- mStackSupervisor.startSpecificActivityLocked(r, noStackActivityResumed, false);
+ mStackSupervisor.startSpecificActivityLocked(r, andResume, false);
return true;
}
}
@@ -1772,15 +1775,17 @@
*
* @param prev The previously resumed activity, for when in the process
* of pausing; can be null to call from elsewhere.
+ * @param options Activity options.
*
* @return Returns true if something is being resumed, or false if
* nothing happened.
+ *
+ * NOTE: It is not safe to call this method directly as it can cause an activity in a
+ * non-focused stack to be resumed.
+ * Use {@link ActivityStackSupervisor#resumeFocusedStackTopActivityLocked} to resume the
+ * right activity for the current system state.
*/
- final boolean resumeTopActivityLocked(ActivityRecord prev) {
- return resumeTopActivityLocked(prev, null);
- }
-
- final boolean resumeTopActivityLocked(ActivityRecord prev, ActivityOptions options) {
+ boolean resumeTopActivityUncheckedLocked(ActivityRecord prev, ActivityOptions options) {
if (mStackSupervisor.inResumeTopActivity) {
// Don't even start recursing.
return false;
@@ -1836,7 +1841,7 @@
// stack is not covering the entire screen.
final ActivityStack stack = getNextVisibleStackLocked();
if (adjustFocusToNextVisibleStackLocked(stack, reason)) {
- return mStackSupervisor.resumeTopActivitiesLocked(stack, prev, null);
+ return mStackSupervisor.resumeFocusedStackTopActivityLocked(stack, prev, null);
}
}
// Let's just start up the Launcher...
@@ -2897,10 +2902,7 @@
}
}
- final ActivityRecord top = mStackSupervisor.topRunningActivityLocked();
- if (top != null) {
- mService.setFocusedActivityLocked(top, myReason);
- }
+ mService.setFocusedActivityLocked(mStackSupervisor.topRunningActivityLocked(), myReason);
}
private boolean adjustFocusToNextVisibleStackLocked(ActivityStack inStack, String reason) {
@@ -2909,12 +2911,7 @@
if (stack == null) {
return false;
}
- final ActivityRecord top = stack.topRunningActivityLocked();
- if (top == null) {
- return false;
- }
- mService.setFocusedActivityLocked(top, myReason);
- return true;
+ return mService.setFocusedActivityLocked(stack.topRunningActivityLocked(), myReason);
}
final void stopActivityLocked(ActivityRecord r) {
@@ -3224,7 +3221,7 @@
r.makeFinishingLocked();
boolean activityRemoved = destroyActivityLocked(r, true, "finish-imm");
if (activityRemoved) {
- mStackSupervisor.resumeTopActivitiesLocked();
+ mStackSupervisor.resumeFocusedStackTopActivityLocked();
}
if (DEBUG_CONTAINERS) Slog.d(TAG_CONTAINERS,
"destroyActivityLocked: finishCurrentActivityLocked r=" + r +
@@ -3237,7 +3234,7 @@
if (DEBUG_ALL) Slog.v(TAG, "Enqueueing pending finish: " + r);
mStackSupervisor.mFinishingActivities.add(r);
r.resumeKeyDispatchingLocked();
- mStackSupervisor.getFocusedStack().resumeTopActivityLocked(null);
+ mStackSupervisor.resumeFocusedStackTopActivityLocked();
return r;
}
@@ -3395,7 +3392,7 @@
if (mPausingActivity == r) {
mPausingActivity = null;
}
- mService.clearFocusedActivity(r);
+ mService.resetFocusedActivityIfNeededLocked(r);
r.configDestroy = false;
r.frozenBeforeDestroy = false;
@@ -3526,7 +3523,7 @@
}
}
if (activityRemoved) {
- mStackSupervisor.resumeTopActivitiesLocked();
+ mStackSupervisor.resumeFocusedStackTopActivityLocked();
}
}
@@ -3700,7 +3697,7 @@
removeActivityFromHistoryLocked(r, reason);
}
}
- mStackSupervisor.resumeTopActivitiesLocked();
+ mStackSupervisor.resumeFocusedStackTopActivityLocked();
} finally {
Binder.restoreCallingIdentity(origId);
}
@@ -3738,7 +3735,7 @@
setVisibleBehindActivity(null);
mStackSupervisor.scheduleIdleTimeoutLocked(null);
}
- mStackSupervisor.resumeTopActivitiesLocked();
+ mStackSupervisor.resumeFocusedStackTopActivityLocked();
}
boolean hasVisibleBehindActivity() {
@@ -3953,7 +3950,7 @@
updateTransitLocked(AppTransition.TRANSIT_TASK_TO_FRONT, options);
}
- mStackSupervisor.resumeTopActivitiesLocked();
+ mStackSupervisor.resumeFocusedStackTopActivityLocked();
EventLog.writeEvent(EventLogTags.AM_TASK_TO_FRONT, tr.userId, tr.taskId);
if (VALIDATE_TOKENS) {
@@ -4016,7 +4013,7 @@
if (fullscreenStack != null && fullscreenStack.hasVisibleBehindActivity()) {
final ActivityRecord visibleBehind = fullscreenStack.getVisibleBehindActivity();
mService.setFocusedActivityLocked(visibleBehind, "moveTaskToBack");
- mStackSupervisor.resumeTopActivitiesLocked(fullscreenStack, null, null);
+ mStackSupervisor.resumeFocusedStackTopActivityLocked();
return true;
}
}
@@ -4071,7 +4068,7 @@
return mStackSupervisor.resumeHomeStackTask(taskToReturnTo, null, "moveTaskToBack");
}
- mStackSupervisor.resumeTopActivitiesLocked();
+ mStackSupervisor.resumeFocusedStackTopActivityLocked();
return true;
}
@@ -4734,10 +4731,7 @@
private void postAddTask(TaskRecord task, ActivityStack prevStack) {
if (prevStack != null) {
- if (prevStack != this
- && (prevStack.mStackId == PINNED_STACK_ID || mStackId == PINNED_STACK_ID)) {
- task.reportPictureInPictureModeChange();
- }
+ task.reportPictureInPictureModeChangeIfNeeded(prevStack);
} else if (task.voiceSession != null) {
try {
task.voiceSession.taskStarted(task.intent, task.taskId);
@@ -4758,25 +4752,21 @@
r.taskConfigOverride = task.mOverrideConfig;
}
- void setFocusAndResumeStateIfNeeded(
- ActivityRecord r, boolean setFocus, boolean setResume, String reason) {
- // If the activity had focus before move focus to this stack.
- if (setFocus) {
- // If the activity owns the last resumed activity, transfer that together,
- // so that we don't resume the same activity again in the new stack.
- // Apps may depend on onResume()/onPause() being called in pairs.
- if (setResume) {
- mResumedActivity = r;
- // Move the stack in which we are placing the activity to the front. We don't use
- // ActivityManagerService.setFocusedActivityLocked, because if the activity is
- // already focused, the call will short-circuit and do nothing.
- moveToFront(reason);
- } else {
- // We need to not only move the stack to the front, but also have the activity
- // focused. This will achieve both goals.
- mService.setFocusedActivityLocked(r, reason);
- }
+ void moveToFrontAndResumeStateIfNeeded(
+ ActivityRecord r, boolean moveToFront, boolean setResume, String reason) {
+ if (!moveToFront) {
+ return;
}
+
+ // If the activity owns the last resumed activity, transfer that together,
+ // so that we don't resume the same activity again in the new stack.
+ // Apps may depend on onResume()/onPause() being called in pairs.
+ if (setResume) {
+ mResumedActivity = r;
+ }
+ // Move the stack in which we are placing the activity to the front. The call will also
+ // make sure the activity focus is set.
+ moveToFront(reason);
}
/**
@@ -4800,8 +4790,11 @@
r.setTask(task, null);
task.addActivityToTop(r);
setAppTask(r, task);
- task.reportPictureInPictureModeChange();
- setFocusAndResumeStateIfNeeded(r, wasFocused, wasResumed, "moveActivityToStack");
+ task.reportPictureInPictureModeChangeIfNeeded(prevStack);
+ moveToFrontAndResumeStateIfNeeded(r, wasFocused, wasResumed, "moveActivityToStack");
+ if (wasResumed) {
+ prevStack.mResumedActivity = null;
+ }
}
private void setAppTask(ActivityRecord r, TaskRecord task) {
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index 32671acf..df1aa52 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -370,7 +370,7 @@
*/
private LockTaskNotify mLockTaskNotify;
- /** Used to keep resumeTopActivityLocked() from being entered recursively */
+ /** Used to keep resumeTopActivityUncheckedLocked() from being entered recursively */
boolean inResumeTopActivity;
// temp. rects used during resize calculation so we don't need to create a new object each time.
@@ -537,16 +537,25 @@
return stack == mHomeStack.mStacks.get((mHomeStack.mStacks.size() - 1));
}
+ /** NOTE: Should only be called from {@link ActivityStack#moveToFront} */
void setFocusStack(String reason, ActivityStack focusedStack) {
- mLastFocusedStack = mFocusedStack;
- mFocusedStack = focusedStack;
+ if (focusedStack != mFocusedStack) {
+ mLastFocusedStack = mFocusedStack;
+ mFocusedStack = focusedStack;
- EventLogTags.writeAmFocusedStack(
- mCurrentUser, mFocusedStack == null ? -1 : mFocusedStack.getStackId(),
- mLastFocusedStack == null ? -1 : mLastFocusedStack.getStackId(), reason);
+ EventLogTags.writeAmFocusedStack(
+ mCurrentUser, mFocusedStack == null ? -1 : mFocusedStack.getStackId(),
+ mLastFocusedStack == null ? -1 : mLastFocusedStack.getStackId(), reason);
+ }
+
+ final ActivityRecord r = topRunningActivityLocked();
+ if (mService.mFocusedActivity != r) {
+ // The focus activity should always be the top activity in the focused stack.
+ // There will be chaos and anarchy if it isn't...
+ mService.setFocusedActivityLocked(r, reason + " setFocusStack");
+ }
if (mService.mBooting || !mService.mBooted) {
- final ActivityRecord r = topRunningActivityLocked();
if (r != null && r.idle) {
checkFinishBootingLocked();
}
@@ -591,12 +600,14 @@
mHomeStack.moveHomeStackTaskToTop(homeStackTaskType);
ActivityRecord r = getHomeActivity();
+ final String myReason = reason + " resumeHomeStackTask";
+
// Only resume home activity if isn't finishing.
if (r != null && !r.finishing) {
- mService.setFocusedActivityLocked(r, reason);
- return resumeTopActivitiesLocked(mHomeStack, prev, null);
+ mService.setFocusedActivityLocked(r, myReason);
+ return resumeFocusedStackTopActivityLocked(mHomeStack, prev, null);
}
- return mService.startHomeActivityLocked(mCurrentUser, reason);
+ return mService.startHomeActivityLocked(mCurrentUser, myReason);
}
TaskRecord anyTaskForIdLocked(int id) {
@@ -1148,14 +1159,12 @@
// a resume.
stack.minimalResumeActivityLocked(r);
} else {
- // This activity is not starting in the resumed state... which
- // should look like we asked it to pause+stop (but remain visible),
- // and it has done so and reported back the current icicle and
- // other state.
+ // This activity is not starting in the resumed state... which should look like we asked
+ // it to pause+stop (but remain visible), and it has done so and reported back the
+ // current icicle and other state.
if (DEBUG_STATES) Slog.v(TAG_STATES,
- "Moving to STOPPED: " + r + " (starting in stopped state)");
- r.state = STOPPED;
- r.stopped = true;
+ "Moving to PAUSED: " + r + " (starting in paused state)");
+ r.state = PAUSED;
}
// Launch the new version setup screen if needed. We do this -after-
@@ -1345,14 +1354,14 @@
return ACTIVITY_RESTRICTION_NONE;
}
- boolean setFocusedStack(ActivityRecord r, String reason) {
+ boolean moveActivityStackToFront(ActivityRecord r, String reason) {
if (r == null) {
// Not sure what you are trying to do, but it is not going to work...
return false;
}
final TaskRecord task = r.task;
if (task == null || task.stack == null) {
- Slog.w(TAG, "Can't set focus stack for r=" + r + " task=" + task);
+ Slog.w(TAG, "Can't move stack to front for r=" + r + " task=" + task);
return false;
}
task.stack.moveToFront(reason, task);
@@ -1518,7 +1527,7 @@
//mWindowManager.dump();
if (activityRemoved) {
- resumeTopActivitiesLocked();
+ resumeFocusedStackTopActivityLocked();
}
return r;
@@ -1611,35 +1620,17 @@
}
}
- boolean resumeTopActivitiesLocked() {
- return resumeTopActivitiesLocked(null, null, null);
+ boolean resumeFocusedStackTopActivityLocked() {
+ return resumeFocusedStackTopActivityLocked(null, null, null);
}
- boolean resumeTopActivitiesLocked(
+ boolean resumeFocusedStackTopActivityLocked(
ActivityStack targetStack, ActivityRecord target, ActivityOptions targetOptions) {
- if (targetStack == null) {
- targetStack = mFocusedStack;
+ if (targetStack != null && isFocusedStack(targetStack)) {
+ return targetStack.resumeTopActivityUncheckedLocked(target, targetOptions);
}
- // Do targetStack first.
- boolean result = false;
- if (isFocusedStack(targetStack)) {
- result = targetStack.resumeTopActivityLocked(target, targetOptions);
- }
-
- for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
- final ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
- for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
- final ActivityStack stack = stacks.get(stackNdx);
- if (stack == targetStack) {
- // Already started above.
- continue;
- }
- if (isFocusedStack(stack)) {
- stack.resumeTopActivityLocked(null);
- }
- }
- }
- return result;
+ mFocusedStack.resumeTopActivityUncheckedLocked(null, null);
+ return false;
}
void finishTopRunningActivityLocked(ProcessRecord app, String reason) {
@@ -1905,7 +1896,7 @@
// are made visible with the correct configuration.
ensureActivitiesVisibleLocked(r, 0, preserveWindows);
if (!updated) {
- resumeTopActivitiesLocked(stack, null, null);
+ resumeFocusedStackTopActivityLocked();
}
}
} finally {
@@ -1961,7 +1952,7 @@
// All other activities must be made visible with their correct configuration.
ensureActivitiesVisibleLocked(r, 0, !PRESERVE_WINDOWS);
if (!kept) {
- resumeTopActivitiesLocked(stack, null, null);
+ resumeFocusedStackTopActivityLocked();
}
}
}
@@ -2083,8 +2074,14 @@
ActivityStack moveTaskToStackUncheckedLocked(
TaskRecord task, int stackId, boolean toTop, boolean forceFocus, String reason) {
final ActivityRecord r = task.getTopActivity();
- final boolean wasFocused = isFocusedStack(task.stack) && (topRunningActivityLocked() == r);
- final boolean wasResumed = wasFocused && (task.stack.mResumedActivity == r);
+ final ActivityStack prevStack = task.stack;
+ final boolean wasFocused = isFocusedStack(prevStack) && (topRunningActivityLocked() == r);
+ final boolean wasResumed = wasFocused && (prevStack.mResumedActivity == r);
+ // In some cases the focused stack isn't the front stack. E.g. pinned stack.
+ // Whenever we are moving the top activity from the front stack we want to make sure to move
+ // the stack to the front.
+ final boolean wasFront = isFrontStack(prevStack)
+ && (prevStack.topRunningActivityLocked() == r);
final boolean resizeable = task.mResizeable;
// Temporarily disable resizeablility of task we are moving. We don't want it to be resized
@@ -2097,9 +2094,9 @@
stack.addTask(task, toTop, reason);
// If the task had focus before (or we're requested to move focus),
- // move focus to the new stack.
- stack.setFocusAndResumeStateIfNeeded(
- r, forceFocus || wasFocused, wasResumed, reason);
+ // move focus to the new stack by moving the stack to the front.
+ stack.moveToFrontAndResumeStateIfNeeded(
+ r, forceFocus || wasFocused || wasFront, wasResumed, reason);
return stack;
}
@@ -2149,7 +2146,7 @@
// The task might have already been running and its visibility needs to be synchronized with
// the visibility of the stack / windows.
ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
- resumeTopActivitiesLocked();
+ resumeFocusedStackTopActivityLocked();
if (!task.mResizeable && isStackDockedInEffect(stackId)) {
showNonResizeableDockToast(taskId);
@@ -2200,7 +2197,7 @@
// The task might have already been running and its visibility needs to be synchronized with
// the visibility of the stack / windows.
ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
- resumeTopActivitiesLocked();
+ resumeFocusedStackTopActivityLocked();
}
void positionTaskInStackLocked(int taskId, int stackId, int position) {
@@ -2219,7 +2216,7 @@
// The task might have already been running and its visibility needs to be synchronized with
// the visibility of the stack / windows.
stack.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
- resumeTopActivitiesLocked();
+ resumeFocusedStackTopActivityLocked();
}
ActivityRecord findTaskLocked(ActivityRecord r) {
@@ -2329,7 +2326,7 @@
final ActivityStack stack = stacks.get(stackNdx);
stack.awakeFromSleepingLocked();
if (isFocusedStack(stack)) {
- resumeTopActivitiesLocked();
+ resumeFocusedStackTopActivityLocked();
}
}
}
@@ -3124,7 +3121,7 @@
if (DEBUG_LOCKTASK) Slog.w(TAG_LOCKTASK,
"setLockTaskModeLocked: Tasks remaining, can't unlock");
lockedTask.performClearTaskLocked();
- resumeTopActivitiesLocked();
+ resumeFocusedStackTopActivityLocked();
return;
}
}
@@ -3165,7 +3162,7 @@
if (andResume) {
findTaskToMoveToFrontLocked(task, 0, null, reason);
- resumeTopActivitiesLocked();
+ resumeFocusedStackTopActivityLocked();
}
}
@@ -3233,7 +3230,7 @@
didSomething = true;
}
if (didSomething) {
- resumeTopActivitiesLocked();
+ resumeFocusedStackTopActivityLocked();
}
}
@@ -3280,7 +3277,7 @@
} break;
case RESUME_TOP_ACTIVITY_MSG: {
synchronized (mService) {
- resumeTopActivitiesLocked();
+ resumeFocusedStackTopActivityLocked();
}
} break;
case SLEEP_TIMEOUT_MSG: {
@@ -3661,7 +3658,7 @@
mSurface = surface;
if (surface != null) {
- mStack.resumeTopActivityLocked(null);
+ resumeFocusedStackTopActivityLocked();
} else {
mContainerState = CONTAINER_STATE_NO_SURFACE;
((VirtualActivityDisplay) mActivityDisplay).setSurface(null);
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index b16e160..bcb2215 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -916,7 +916,7 @@
// is the case, so this is it! And for paranoia, make
// sure we have correctly resumed the top activity.
if (doResume) {
- mSupervisor.resumeTopActivitiesLocked(targetStack, null, options);
+ mSupervisor.resumeFocusedStackTopActivityLocked(targetStack, null, options);
// Make sure to notify Keyguard as well if we are not running an app
// transition later.
@@ -1019,7 +1019,7 @@
// don't use that intent!) And for paranoia, make
// sure we have correctly resumed the top activity.
if (doResume) {
- targetStack.resumeTopActivityLocked(null, options);
+ mSupervisor.resumeFocusedStackTopActivityLocked(targetStack, null, options);
if (!movedToFront) {
// Make sure to notify Keyguard as well if we are not running an app
// transition later.
@@ -1055,7 +1055,7 @@
// For paranoia, make sure we have correctly resumed the top activity.
topStack.mLastPausedActivity = null;
if (doResume) {
- mSupervisor.resumeTopActivitiesLocked();
+ mSupervisor.resumeFocusedStackTopActivityLocked();
}
ActivityOptions.abort(options);
if ((startFlags & ActivityManager.START_FLAG_ONLY_IF_NEEDED) != 0) {
@@ -1157,7 +1157,7 @@
// resumed the top activity.
targetStack.mLastPausedActivity = null;
if (doResume) {
- targetStack.resumeTopActivityLocked(null);
+ mSupervisor.resumeFocusedStackTopActivityLocked();
}
ActivityOptions.abort(options);
return ActivityManager.START_DELIVERED_TO_TOP;
@@ -1176,7 +1176,7 @@
top.deliverNewIntentLocked(callingUid, r.intent, r.launchedFromPackage);
targetStack.mLastPausedActivity = null;
if (doResume) {
- targetStack.resumeTopActivityLocked(null);
+ mSupervisor.resumeFocusedStackTopActivityLocked();
}
return ActivityManager.START_DELIVERED_TO_TOP;
}
@@ -1266,9 +1266,11 @@
targetStack.startActivityLocked(r, newTask, keepCurTransition, options);
if (doResume) {
if (!launchTaskBehind) {
+ // TODO: Remove this code after verification that all the decision points above
+ // moved targetStack to the front which will also set the focus activity.
mService.setFocusedActivityLocked(r, "startedActivity");
}
- mSupervisor.resumeTopActivitiesLocked(targetStack, r, options);
+ mSupervisor.resumeFocusedStackTopActivityLocked(targetStack, r, options);
} else {
targetStack.addRecentActivityLocked(r);
}
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index 4647d77..e77bb20 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -1321,7 +1321,12 @@
}
}
- void reportPictureInPictureModeChange() {
+ void reportPictureInPictureModeChangeIfNeeded(ActivityStack prevStack) {
+ if (prevStack == null || prevStack == stack
+ || (prevStack.mStackId != PINNED_STACK_ID && stack.mStackId != PINNED_STACK_ID)) {
+ return;
+ }
+
for (int i = mActivities.size() - 1; i >= 0; i--) {
final ActivityRecord r = mActivities.get(i);
if (r.app != null && r.app.thread != null) {
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 20b1e60..4cdf6b8 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -931,7 +931,7 @@
if (homeInFront) {
mService.startHomeActivityLocked(newUserId, "moveUserToForeground");
} else {
- mService.mStackSupervisor.resumeTopActivitiesLocked();
+ mService.mStackSupervisor.resumeFocusedStackTopActivityLocked();
}
EventLogTags.writeAmSwitchUser(newUserId);
getUserManager().onUserForeground(newUserId);
diff --git a/services/core/java/com/android/server/policy/GlobalActions.java b/services/core/java/com/android/server/policy/GlobalActions.java
index 5948d3c..3eae7fc 100644
--- a/services/core/java/com/android/server/policy/GlobalActions.java
+++ b/services/core/java/com/android/server/policy/GlobalActions.java
@@ -383,13 +383,11 @@
// Add a little delay before executing, to give the
// dialog a chance to go away before it takes a
// screenshot.
- // TODO: remove once screenshots are handled by Shell (instead of dumpstate)
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
try {
- // Take a "heavy" bugreport: it's more user friendly, but causes more
- // interference.
+ // Take an "interactive" bugreport.
ActivityManagerNative.getDefault().requestBugReport(true);
} catch (RemoteException e) {
}
@@ -405,11 +403,11 @@
return false;
}
try {
- // Take a "light" bugreport, with less interference.
+ // Take a "full" bugreport.
ActivityManagerNative.getDefault().requestBugReport(false);
} catch (RemoteException e) {
}
- return true;
+ return false;
}
public boolean showDuringKeyguard() {
diff --git a/telecomm/java/android/telecom/Call.java b/telecomm/java/android/telecom/Call.java
index 92d5aa9..5d5df95 100644
--- a/telecomm/java/android/telecom/Call.java
+++ b/telecomm/java/android/telecom/Call.java
@@ -269,7 +269,7 @@
* @return Whether the specified capability is supported.
*/
public static boolean can(int capabilities, int capability) {
- return (capabilities & capability) != 0;
+ return (capabilities & capability) == capability;
}
/**
@@ -351,7 +351,7 @@
* @return Whether the specified property is supported.
*/
public static boolean hasProperty(int properties, int property) {
- return (properties & property) != 0;
+ return (properties & property) == property;
}
/**
diff --git a/telecomm/java/android/telecom/Connection.java b/telecomm/java/android/telecom/Connection.java
index 17bd08c..deb98f4 100644
--- a/telecomm/java/android/telecom/Connection.java
+++ b/telecomm/java/android/telecom/Connection.java
@@ -294,7 +294,7 @@
* @hide
*/
public static boolean can(int capabilities, int capability) {
- return (capabilities & capability) != 0;
+ return (capabilities & capability) == capability;
}
/**
diff --git a/telephony/java/android/telephony/ServiceState.java b/telephony/java/android/telephony/ServiceState.java
index 8537f9c..c680999 100644
--- a/telephony/java/android/telephony/ServiceState.java
+++ b/telephony/java/android/telephony/ServiceState.java
@@ -153,6 +153,17 @@
* @hide
*/
public static final int RIL_RADIO_TECHNOLOGY_IWLAN = 18;
+
+ /** @hide */
+ public static final int RIL_RADIO_CDMA_TECHNOLOGY_BITMASK =
+ (1 << (RIL_RADIO_TECHNOLOGY_IS95A - 1))
+ | (1 << (RIL_RADIO_TECHNOLOGY_IS95B - 1))
+ | (1 << (RIL_RADIO_TECHNOLOGY_1xRTT - 1))
+ | (1 << (RIL_RADIO_TECHNOLOGY_EVDO_0 - 1))
+ | (1 << (RIL_RADIO_TECHNOLOGY_EVDO_A - 1))
+ | (1 << (RIL_RADIO_TECHNOLOGY_EVDO_B - 1))
+ | (1 << (RIL_RADIO_TECHNOLOGY_EHRPD - 1));
+
/**
* Available registration states for GSM, UMTS and CDMA.
*/
@@ -1141,16 +1152,8 @@
}
/** @hide */
- public static boolean hasCdma(int radioTechnologyBitmask) {
- int cdmaBitmask = (RIL_RADIO_TECHNOLOGY_IS95A
- | RIL_RADIO_TECHNOLOGY_IS95B
- | RIL_RADIO_TECHNOLOGY_1xRTT
- | RIL_RADIO_TECHNOLOGY_EVDO_0
- | RIL_RADIO_TECHNOLOGY_EVDO_A
- | RIL_RADIO_TECHNOLOGY_EVDO_B
- | RIL_RADIO_TECHNOLOGY_EHRPD);
-
- return ((radioTechnologyBitmask & cdmaBitmask) != 0);
+ public static boolean bearerBitmapHasCdma(int radioTechnologyBitmap) {
+ return (RIL_RADIO_CDMA_TECHNOLOGY_BITMASK & radioTechnologyBitmap) != 0;
}
/** @hide */