Merge "fix ActivityMetricsLogger counter names"
diff --git a/Android.mk b/Android.mk
index d3e1cf5..aff6cf2 100644
--- a/Android.mk
+++ b/Android.mk
@@ -226,7 +226,6 @@
core/java/android/service/carrier/ICarrierMessagingService.aidl \
core/java/android/service/gatekeeper/IGateKeeperService.aidl \
core/java/android/service/notification/INotificationListener.aidl \
- core/java/android/service/notification/INotificationAssistant.aidl \
core/java/android/service/notification/IStatusBarNotificationHolder.aidl \
core/java/android/service/notification/IConditionListener.aidl \
core/java/android/service/notification/IConditionProvider.aidl \
diff --git a/api/current.txt b/api/current.txt
index 3854b7a..2f0c706 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);
}
@@ -9568,8 +9571,8 @@
field public static final java.lang.String FEATURE_WIFI_DIRECT = "android.hardware.wifi.direct";
field public static final int GET_ACTIVITIES = 1; // 0x1
field public static final int GET_CONFIGURATIONS = 16384; // 0x4000
- field public static final int GET_DISABLED_COMPONENTS = 512; // 0x200
- field public static final int GET_DISABLED_UNTIL_USED_COMPONENTS = 32768; // 0x8000
+ field public static final deprecated int GET_DISABLED_COMPONENTS = 512; // 0x200
+ field public static final deprecated int GET_DISABLED_UNTIL_USED_COMPONENTS = 32768; // 0x8000
field public static final int GET_GIDS = 256; // 0x100
field public static final int GET_INSTRUMENTATION = 16; // 0x10
field public static final int GET_INTENT_FILTERS = 32; // 0x20
@@ -9581,13 +9584,17 @@
field public static final int GET_SERVICES = 4; // 0x4
field public static final int GET_SHARED_LIBRARY_FILES = 1024; // 0x400
field public static final int GET_SIGNATURES = 64; // 0x40
- field public static final int GET_UNINSTALLED_PACKAGES = 8192; // 0x2000
+ field public static final deprecated int GET_UNINSTALLED_PACKAGES = 8192; // 0x2000
field public static final int GET_URI_PERMISSION_PATTERNS = 2048; // 0x800
field public static final int MATCH_ALL = 131072; // 0x20000
field public static final int MATCH_DEFAULT_ONLY = 65536; // 0x10000
+ field public static final int MATCH_DISABLED_COMPONENTS = 512; // 0x200
+ field public static final int MATCH_DISABLED_UNTIL_USED_COMPONENTS = 32768; // 0x8000
+ field public static final int MATCH_ENCRYPTION_AWARE = 524288; // 0x80000
field public static final int MATCH_ENCRYPTION_AWARE_AND_UNAWARE = 786432; // 0xc0000
- field public static final int MATCH_ENCRYPTION_AWARE_ONLY = 524288; // 0x80000
- field public static final int MATCH_ENCRYPTION_UNAWARE_ONLY = 262144; // 0x40000
+ field public static final int MATCH_ENCRYPTION_UNAWARE = 262144; // 0x40000
+ field public static final int MATCH_SYSTEM_ONLY = 1048576; // 0x100000
+ field public static final int MATCH_UNINSTALLED_PACKAGES = 8192; // 0x2000
field public static final long MAXIMUM_VERIFICATION_TIMEOUT = 3600000L; // 0x36ee80L
field public static final int PERMISSION_DENIED = -1; // 0xffffffff
field public static final int PERMISSION_GRANTED = 0; // 0x0
@@ -33491,20 +33498,13 @@
field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.ConditionProviderService";
}
- public class NotificationAdjustment implements android.os.Parcelable {
- ctor public NotificationAdjustment(int, java.lang.CharSequence, android.net.Uri);
- method public int describeContents();
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator<android.service.notification.NotificationAdjustment> CREATOR;
- }
-
public abstract class NotificationAssistantService extends android.service.notification.NotificationListenerService {
ctor public NotificationAssistantService();
- method public final void adjustImportance(java.lang.String, android.service.notification.NotificationAdjustment);
+ method public final void adjustImportance(java.lang.String, android.service.notification.NotificationAssistantService.Adjustment);
method public final void clearAnnotation(java.lang.String);
method public void onNotificationActionClick(java.lang.String, long, int);
method public void onNotificationClick(java.lang.String, long);
- method public abstract android.service.notification.NotificationAdjustment onNotificationEnqueued(android.service.notification.StatusBarNotification, int, boolean);
+ method public abstract android.service.notification.NotificationAssistantService.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification, int, boolean);
method public void onNotificationRemoved(java.lang.String, long, int);
method public void onNotificationVisibilityChanged(java.lang.String, long, boolean);
method public final void setAnnotation(java.lang.String, android.app.Notification);
@@ -33524,6 +33524,10 @@
field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.NotificationAssistantService";
}
+ public class NotificationAssistantService.Adjustment {
+ ctor public NotificationAssistantService.Adjustment(int, java.lang.CharSequence, android.net.Uri);
+ }
+
public abstract class NotificationListenerService extends android.app.Service {
ctor public NotificationListenerService();
method public final void cancelAllNotifications();
diff --git a/api/system-current.txt b/api/system-current.txt
index 2990516..81831f87 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);
}
@@ -9884,8 +9887,8 @@
field public static final java.lang.String FEATURE_WIFI_DIRECT = "android.hardware.wifi.direct";
field public static final int GET_ACTIVITIES = 1; // 0x1
field public static final int GET_CONFIGURATIONS = 16384; // 0x4000
- field public static final int GET_DISABLED_COMPONENTS = 512; // 0x200
- field public static final int GET_DISABLED_UNTIL_USED_COMPONENTS = 32768; // 0x8000
+ field public static final deprecated int GET_DISABLED_COMPONENTS = 512; // 0x200
+ field public static final deprecated int GET_DISABLED_UNTIL_USED_COMPONENTS = 32768; // 0x8000
field public static final int GET_GIDS = 256; // 0x100
field public static final int GET_INSTRUMENTATION = 16; // 0x10
field public static final int GET_INTENT_FILTERS = 32; // 0x20
@@ -9897,7 +9900,7 @@
field public static final int GET_SERVICES = 4; // 0x4
field public static final int GET_SHARED_LIBRARY_FILES = 1024; // 0x400
field public static final int GET_SIGNATURES = 64; // 0x40
- field public static final int GET_UNINSTALLED_PACKAGES = 8192; // 0x2000
+ field public static final deprecated int GET_UNINSTALLED_PACKAGES = 8192; // 0x2000
field public static final int GET_URI_PERMISSION_PATTERNS = 2048; // 0x800
field public static final int INSTALL_FAILED_ALREADY_EXISTS = -1; // 0xffffffff
field public static final int INSTALL_FAILED_CONFLICTING_PROVIDER = -13; // 0xfffffff3
@@ -9938,9 +9941,13 @@
field public static final int MASK_PERMISSION_FLAGS = 255; // 0xff
field public static final int MATCH_ALL = 131072; // 0x20000
field public static final int MATCH_DEFAULT_ONLY = 65536; // 0x10000
+ field public static final int MATCH_DISABLED_COMPONENTS = 512; // 0x200
+ field public static final int MATCH_DISABLED_UNTIL_USED_COMPONENTS = 32768; // 0x8000
+ field public static final int MATCH_ENCRYPTION_AWARE = 524288; // 0x80000
field public static final int MATCH_ENCRYPTION_AWARE_AND_UNAWARE = 786432; // 0xc0000
- field public static final int MATCH_ENCRYPTION_AWARE_ONLY = 524288; // 0x80000
- field public static final int MATCH_ENCRYPTION_UNAWARE_ONLY = 262144; // 0x40000
+ field public static final int MATCH_ENCRYPTION_UNAWARE = 262144; // 0x40000
+ field public static final int MATCH_SYSTEM_ONLY = 1048576; // 0x100000
+ field public static final int MATCH_UNINSTALLED_PACKAGES = 8192; // 0x2000
field public static final long MAXIMUM_VERIFICATION_TIMEOUT = 3600000L; // 0x36ee80L
field public static final int PERMISSION_DENIED = -1; // 0xffffffff
field public static final int PERMISSION_GRANTED = 0; // 0x0
@@ -35633,20 +35640,13 @@
field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.ConditionProviderService";
}
- public class NotificationAdjustment implements android.os.Parcelable {
- ctor public NotificationAdjustment(int, java.lang.CharSequence, android.net.Uri);
- method public int describeContents();
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator<android.service.notification.NotificationAdjustment> CREATOR;
- }
-
public abstract class NotificationAssistantService extends android.service.notification.NotificationListenerService {
ctor public NotificationAssistantService();
- method public final void adjustImportance(java.lang.String, android.service.notification.NotificationAdjustment);
+ method public final void adjustImportance(java.lang.String, android.service.notification.NotificationAssistantService.Adjustment);
method public final void clearAnnotation(java.lang.String);
method public void onNotificationActionClick(java.lang.String, long, int);
method public void onNotificationClick(java.lang.String, long);
- method public abstract android.service.notification.NotificationAdjustment onNotificationEnqueued(android.service.notification.StatusBarNotification, int, boolean);
+ method public abstract android.service.notification.NotificationAssistantService.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification, int, boolean);
method public void onNotificationRemoved(java.lang.String, long, int);
method public void onNotificationVisibilityChanged(java.lang.String, long, boolean);
method public final void setAnnotation(java.lang.String, android.app.Notification);
@@ -35666,6 +35666,10 @@
field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.NotificationAssistantService";
}
+ public class NotificationAssistantService.Adjustment {
+ ctor public NotificationAssistantService.Adjustment(int, java.lang.CharSequence, android.net.Uri);
+ }
+
public abstract class NotificationListenerService extends android.app.Service {
ctor public NotificationListenerService();
method public final void cancelAllNotifications();
diff --git a/api/test-current.txt b/api/test-current.txt
index 83d12fc..c155278 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);
}
@@ -9568,8 +9571,8 @@
field public static final java.lang.String FEATURE_WIFI_DIRECT = "android.hardware.wifi.direct";
field public static final int GET_ACTIVITIES = 1; // 0x1
field public static final int GET_CONFIGURATIONS = 16384; // 0x4000
- field public static final int GET_DISABLED_COMPONENTS = 512; // 0x200
- field public static final int GET_DISABLED_UNTIL_USED_COMPONENTS = 32768; // 0x8000
+ field public static final deprecated int GET_DISABLED_COMPONENTS = 512; // 0x200
+ field public static final deprecated int GET_DISABLED_UNTIL_USED_COMPONENTS = 32768; // 0x8000
field public static final int GET_GIDS = 256; // 0x100
field public static final int GET_INSTRUMENTATION = 16; // 0x10
field public static final int GET_INTENT_FILTERS = 32; // 0x20
@@ -9581,13 +9584,17 @@
field public static final int GET_SERVICES = 4; // 0x4
field public static final int GET_SHARED_LIBRARY_FILES = 1024; // 0x400
field public static final int GET_SIGNATURES = 64; // 0x40
- field public static final int GET_UNINSTALLED_PACKAGES = 8192; // 0x2000
+ field public static final deprecated int GET_UNINSTALLED_PACKAGES = 8192; // 0x2000
field public static final int GET_URI_PERMISSION_PATTERNS = 2048; // 0x800
field public static final int MATCH_ALL = 131072; // 0x20000
field public static final int MATCH_DEFAULT_ONLY = 65536; // 0x10000
+ field public static final int MATCH_DISABLED_COMPONENTS = 512; // 0x200
+ field public static final int MATCH_DISABLED_UNTIL_USED_COMPONENTS = 32768; // 0x8000
+ field public static final int MATCH_ENCRYPTION_AWARE = 524288; // 0x80000
field public static final int MATCH_ENCRYPTION_AWARE_AND_UNAWARE = 786432; // 0xc0000
- field public static final int MATCH_ENCRYPTION_AWARE_ONLY = 524288; // 0x80000
- field public static final int MATCH_ENCRYPTION_UNAWARE_ONLY = 262144; // 0x40000
+ field public static final int MATCH_ENCRYPTION_UNAWARE = 262144; // 0x40000
+ field public static final int MATCH_SYSTEM_ONLY = 1048576; // 0x100000
+ field public static final int MATCH_UNINSTALLED_PACKAGES = 8192; // 0x2000
field public static final long MAXIMUM_VERIFICATION_TIMEOUT = 3600000L; // 0x36ee80L
field public static final int PERMISSION_DENIED = -1; // 0xffffffff
field public static final int PERMISSION_GRANTED = 0; // 0x0
@@ -33494,20 +33501,13 @@
field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.ConditionProviderService";
}
- public class NotificationAdjustment implements android.os.Parcelable {
- ctor public NotificationAdjustment(int, java.lang.CharSequence, android.net.Uri);
- method public int describeContents();
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator<android.service.notification.NotificationAdjustment> CREATOR;
- }
-
public abstract class NotificationAssistantService extends android.service.notification.NotificationListenerService {
ctor public NotificationAssistantService();
- method public final void adjustImportance(java.lang.String, android.service.notification.NotificationAdjustment);
+ method public final void adjustImportance(java.lang.String, android.service.notification.NotificationAssistantService.Adjustment);
method public final void clearAnnotation(java.lang.String);
method public void onNotificationActionClick(java.lang.String, long, int);
method public void onNotificationClick(java.lang.String, long);
- method public abstract android.service.notification.NotificationAdjustment onNotificationEnqueued(android.service.notification.StatusBarNotification, int, boolean);
+ method public abstract android.service.notification.NotificationAssistantService.Adjustment onNotificationEnqueued(android.service.notification.StatusBarNotification, int, boolean);
method public void onNotificationRemoved(java.lang.String, long, int);
method public void onNotificationVisibilityChanged(java.lang.String, long, boolean);
method public final void setAnnotation(java.lang.String, android.app.Notification);
@@ -33527,6 +33527,10 @@
field public static final java.lang.String SERVICE_INTERFACE = "android.service.notification.NotificationAssistantService";
}
+ public class NotificationAssistantService.Adjustment {
+ ctor public NotificationAssistantService.Adjustment(int, java.lang.CharSequence, android.net.Uri);
+ }
+
public abstract class NotificationListenerService extends android.app.Service {
ctor public NotificationListenerService();
method public final void cancelAllNotifications();
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/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl
index abe9822..e60cb03 100644
--- a/core/java/android/app/INotificationManager.aidl
+++ b/core/java/android/app/INotificationManager.aidl
@@ -78,6 +78,8 @@
void setOnNotificationPostedTrimFromListener(in INotificationListener token, int trim);
void setInterruptionFilter(String pkg, int interruptionFilter);
+ void setImportanceFromAssistant(in INotificationListener token, String key, int importance, CharSequence explanation);
+
ComponentName getEffectsSuppressor();
boolean matchesCallFilter(in Bundle extras);
boolean isSystemConditionProviderEnabled(String path);
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 0f3ca10..c3dfab4 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -4169,11 +4169,18 @@
/**
* Internal flag used to indicate that a system component has done their
- * homework and verified their encryption-aware behavior.
+ * homework and verified that they correctly handle packages and components
+ * that come and go over time. In particular:
+ * <ul>
+ * <li>Apps installed on external storage, which will appear to be
+ * uninstalled while the the device is ejected.
+ * <li>Apps with encryption unaware components, which will appear to not
+ * exist while the device is locked.
+ * </ul>
*
* @hide
*/
- public static final int FLAG_DEBUG_ENCRYPTION_TRIAGED = 0x00000100;
+ public static final int FLAG_DEBUG_TRIAGED_MISSING = 0x00000100;
/**
* If set, the new activity is not kept in the history stack. As soon as
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 69f508e..e0855de 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -93,6 +93,109 @@
}
/**
+ * As a guiding principle:
+ * <p>
+ * {@code GET_} flags are used to request additional data that may have been
+ * elided to save wire space.
+ * <p>
+ * {@code MATCH_} flags are used to include components or packages that
+ * would have otherwise been omitted from a result set by current system
+ * state.
+ */
+
+ /** @hide */
+ @IntDef(flag = true, value = {
+ GET_ACTIVITIES,
+ GET_RECEIVERS,
+ GET_SERVICES,
+ GET_PROVIDERS,
+ GET_INSTRUMENTATION,
+ GET_INTENT_FILTERS,
+ GET_SIGNATURES,
+ GET_META_DATA,
+ GET_GIDS,
+ GET_SHARED_LIBRARY_FILES,
+ GET_URI_PERMISSION_PATTERNS,
+ GET_PERMISSIONS,
+ GET_CONFIGURATIONS,
+ MATCH_UNINSTALLED_PACKAGES,
+ MATCH_DISABLED_COMPONENTS,
+ MATCH_DISABLED_UNTIL_USED_COMPONENTS,
+ MATCH_DEBUG_TRIAGED_MISSING,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface PackageInfoFlags {}
+
+ /** @hide */
+ @IntDef(flag = true, value = {
+ GET_META_DATA,
+ GET_SHARED_LIBRARY_FILES,
+ MATCH_UNINSTALLED_PACKAGES,
+ MATCH_SYSTEM_ONLY,
+ MATCH_DEBUG_TRIAGED_MISSING,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ApplicationInfoFlags {}
+
+ /** @hide */
+ @IntDef(flag = true, value = {
+ GET_META_DATA,
+ GET_SHARED_LIBRARY_FILES,
+ MATCH_UNINSTALLED_PACKAGES,
+ MATCH_DISABLED_COMPONENTS,
+ MATCH_DISABLED_UNTIL_USED_COMPONENTS,
+ MATCH_ALL,
+ MATCH_DEFAULT_ONLY,
+ MATCH_ENCRYPTION_AWARE,
+ MATCH_ENCRYPTION_AWARE_AND_UNAWARE,
+ MATCH_ENCRYPTION_UNAWARE,
+ MATCH_SYSTEM_ONLY,
+ MATCH_DEBUG_TRIAGED_MISSING,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ComponentInfoFlags {}
+
+ /** @hide */
+ @IntDef(flag = true, value = {
+ GET_META_DATA,
+ GET_SHARED_LIBRARY_FILES,
+ GET_RESOLVED_FILTER,
+ MATCH_UNINSTALLED_PACKAGES,
+ MATCH_DISABLED_COMPONENTS,
+ MATCH_DISABLED_UNTIL_USED_COMPONENTS,
+ MATCH_ALL,
+ MATCH_DEFAULT_ONLY,
+ MATCH_ENCRYPTION_AWARE,
+ MATCH_ENCRYPTION_AWARE_AND_UNAWARE,
+ MATCH_ENCRYPTION_UNAWARE,
+ MATCH_SYSTEM_ONLY,
+ MATCH_DEBUG_TRIAGED_MISSING,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ResolveInfoFlags {}
+
+ /** @hide */
+ @IntDef(flag = true, value = {
+ GET_META_DATA,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface PermissionInfoFlags {}
+
+ /** @hide */
+ @IntDef(flag = true, value = {
+ GET_META_DATA,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface PermissionGroupInfoFlags {}
+
+ /** @hide */
+ @IntDef(flag = true, value = {
+ GET_META_DATA,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface InstrumentationInfoFlags {}
+
+ /**
* {@link PackageInfo} flag: return information about
* activities in the package in {@link PackageInfo#activities}.
*/
@@ -161,9 +264,15 @@
public static final int GET_GIDS = 0x00000100;
/**
+ * @deprecated replaced with {@link #MATCH_DISABLED_COMPONENTS}
+ */
+ @Deprecated
+ public static final int GET_DISABLED_COMPONENTS = 0x00000200;
+
+ /**
* {@link PackageInfo} flag: include disabled components in the returned info.
*/
- public static final int GET_DISABLED_COMPONENTS = 0x00000200;
+ public static final int MATCH_DISABLED_COMPONENTS = 0x00000200;
/**
* {@link ApplicationInfo} flag: return the
@@ -190,6 +299,12 @@
public static final int GET_PERMISSIONS = 0x00001000;
/**
+ * @deprecated replaced with {@link #MATCH_UNINSTALLED_PACKAGES}
+ */
+ @Deprecated
+ public static final int GET_UNINSTALLED_PACKAGES = 0x00002000;
+
+ /**
* Flag parameter to retrieve some information about all applications (even
* uninstalled ones) which have data directories. This state could have
* resulted if applications have been deleted with flag
@@ -199,7 +314,7 @@
* Note: this flag may cause less information about currently installed
* applications to be returned.
*/
- public static final int GET_UNINSTALLED_PACKAGES = 0x00002000;
+ public static final int MATCH_UNINSTALLED_PACKAGES = 0x00002000;
/**
* {@link PackageInfo} flag: return information about
@@ -211,12 +326,18 @@
public static final int GET_CONFIGURATIONS = 0x00004000;
/**
+ * @deprecated replaced with {@link #MATCH_DISABLED_UNTIL_USED_COMPONENTS}.
+ */
+ @Deprecated
+ public static final int GET_DISABLED_UNTIL_USED_COMPONENTS = 0x00008000;
+
+ /**
* {@link PackageInfo} flag: include disabled components which are in
* that state only because of {@link #COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED}
* in the returned info. Note that if you set this flag, applications
* that are in this disabled state will be reported as enabled.
*/
- public static final int GET_DISABLED_UNTIL_USED_COMPONENTS = 0x00008000;
+ public static final int MATCH_DISABLED_UNTIL_USED_COMPONENTS = 0x00008000;
/**
* Resolution and querying flag: if set, only filters that support the
@@ -227,48 +348,55 @@
public static final int MATCH_DEFAULT_ONLY = 0x00010000;
/**
- * Querying flag: if set and if the platform is doing any filtering of the results, then
- * the filtering will not happen. This is a synonym for saying that all results should
- * be returned.
+ * Querying flag: if set and if the platform is doing any filtering of the
+ * results, then the filtering will not happen. This is a synonym for saying
+ * that all results should be returned.
+ * <p>
+ * <em>This flag should be used with extreme care.</em>
*/
public static final int MATCH_ALL = 0x00020000;
/**
- * {@link PackageInfo} flag: include only components which are encryption
- * unaware in the returned info, regardless of the current user state.
+ * Querying flag: include only components which are encryption unaware in
+ * the returned info, regardless of the current user state.
*/
- public static final int MATCH_ENCRYPTION_UNAWARE_ONLY = 0x00040000;
+ public static final int MATCH_ENCRYPTION_UNAWARE = 0x00040000;
/**
- * {@link PackageInfo} flag: include only components which are encryption
- * aware in the returned info, regardless of the current user state.
+ * Querying flag: include only components which are encryption aware in the
+ * returned info, regardless of the current user state.
*/
- public static final int MATCH_ENCRYPTION_AWARE_ONLY = 0x00080000;
+ public static final int MATCH_ENCRYPTION_AWARE = 0x00080000;
/**
- * {@link PackageInfo} flag: include both encryption aware and unaware
- * components in the returned info, regardless of the current user state.
+ * Querying flag: include both encryption aware and unaware components in
+ * the returned info, regardless of the current user state.
*/
- public static final int MATCH_ENCRYPTION_AWARE_AND_UNAWARE = MATCH_ENCRYPTION_AWARE_ONLY
- | MATCH_ENCRYPTION_UNAWARE_ONLY;
+ public static final int MATCH_ENCRYPTION_AWARE_AND_UNAWARE = MATCH_ENCRYPTION_AWARE
+ | MATCH_ENCRYPTION_UNAWARE;
/**
- * {@link PackageInfo} flag: include only components from applications that
- * are marked with {@link ApplicationInfo#FLAG_SYSTEM}.
- *
- * @hide
+ * Querying flag: include only components from applications that are marked
+ * with {@link ApplicationInfo#FLAG_SYSTEM}.
*/
public static final int MATCH_SYSTEM_ONLY = 0x00100000;
/**
- * {@link PackageInfo} flag: use the default encryption matching behavior
- * based on user state. Internal flag used to indicate that a system
- * component has done their homework and verified their encryption-aware
- * behavior.
+ * Internal flag used to indicate that a system component has done their
+ * homework and verified that they correctly handle packages and components
+ * that come and go over time. In particular:
+ * <ul>
+ * <li>Apps installed on external storage, which will appear to be
+ * uninstalled while the the device is ejected.
+ * <li>Apps with encryption unaware components, which will appear to not
+ * exist while the device is locked.
+ * </ul>
*
+ * @see #MATCH_UNINSTALLED_PACKAGES
+ * @see #MATCH_ENCRYPTION_AWARE_AND_UNAWARE
* @hide
*/
- public static final int MATCH_ENCRYPTION_DEFAULT = 0x10000000;
+ public static final int MATCH_DEBUG_TRIAGED_MISSING = 0x10000000;
/**
* Flag for {@link addCrossProfileIntentFilter}: if this flag is set:
@@ -2131,7 +2259,7 @@
* @see #GET_SIGNATURES
* @see #GET_UNINSTALLED_PACKAGES
*/
- public abstract PackageInfo getPackageInfo(String packageName, int flags)
+ public abstract PackageInfo getPackageInfo(String packageName, @PackageInfoFlags int flags)
throws NameNotFoundException;
/**
@@ -2171,8 +2299,8 @@
* @see #GET_UNINSTALLED_PACKAGES
*/
@RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS)
- public abstract PackageInfo getPackageInfoAsUser(String packageName, int flags, int userId)
- throws NameNotFoundException;
+ public abstract PackageInfo getPackageInfoAsUser(String packageName,
+ @PackageInfoFlags int flags, int userId) throws NameNotFoundException;
/**
* Map from the current package names in use on the device to whatever
@@ -2270,7 +2398,7 @@
* @return Returns a {@link PermissionInfo} containing information about the
* permission.
*/
- public abstract PermissionInfo getPermissionInfo(String name, int flags)
+ public abstract PermissionInfo getPermissionInfo(String name, @PermissionInfoFlags int flags)
throws NameNotFoundException;
/**
@@ -2289,7 +2417,7 @@
* about all of the permissions in the given group.
*/
public abstract List<PermissionInfo> queryPermissionsByGroup(String group,
- int flags) throws NameNotFoundException;
+ @PermissionInfoFlags int flags) throws NameNotFoundException;
/**
* Retrieve all of the information we know about a particular group of
@@ -2307,7 +2435,7 @@
* about the permission.
*/
public abstract PermissionGroupInfo getPermissionGroupInfo(String name,
- int flags) throws NameNotFoundException;
+ @PermissionGroupInfoFlags int flags) throws NameNotFoundException;
/**
* Retrieve all of the known permission groups in the system.
@@ -2318,7 +2446,8 @@
* @return Returns a list of {@link PermissionGroupInfo} containing
* information about all of the known permission groups.
*/
- public abstract List<PermissionGroupInfo> getAllPermissionGroups(int flags);
+ public abstract List<PermissionGroupInfo> getAllPermissionGroups(
+ @PermissionGroupInfoFlags int flags);
/**
* Retrieve all of the information we know about a particular
@@ -2348,7 +2477,7 @@
* @see #GET_UNINSTALLED_PACKAGES
*/
public abstract ApplicationInfo getApplicationInfo(String packageName,
- int flags) throws NameNotFoundException;
+ @ApplicationInfoFlags int flags) throws NameNotFoundException;
/**
* Retrieve all of the information we know about a particular activity
@@ -2371,7 +2500,7 @@
* @see #GET_SHARED_LIBRARY_FILES
*/
public abstract ActivityInfo getActivityInfo(ComponentName component,
- int flags) throws NameNotFoundException;
+ @ComponentInfoFlags int flags) throws NameNotFoundException;
/**
* Retrieve all of the information we know about a particular receiver
@@ -2394,7 +2523,7 @@
* @see #GET_SHARED_LIBRARY_FILES
*/
public abstract ActivityInfo getReceiverInfo(ComponentName component,
- int flags) throws NameNotFoundException;
+ @ComponentInfoFlags int flags) throws NameNotFoundException;
/**
* Retrieve all of the information we know about a particular service
@@ -2416,7 +2545,7 @@
* @see #GET_SHARED_LIBRARY_FILES
*/
public abstract ServiceInfo getServiceInfo(ComponentName component,
- int flags) throws NameNotFoundException;
+ @ComponentInfoFlags int flags) throws NameNotFoundException;
/**
* Retrieve all of the information we know about a particular content
@@ -2438,7 +2567,7 @@
* @see #GET_SHARED_LIBRARY_FILES
*/
public abstract ProviderInfo getProviderInfo(ComponentName component,
- int flags) throws NameNotFoundException;
+ @ComponentInfoFlags int flags) throws NameNotFoundException;
/**
* Return a List of all packages that are installed
@@ -2474,7 +2603,7 @@
* @see #GET_SIGNATURES
* @see #GET_UNINSTALLED_PACKAGES
*/
- public abstract List<PackageInfo> getInstalledPackages(int flags);
+ public abstract List<PackageInfo> getInstalledPackages(@PackageInfoFlags int flags);
/**
* Return a List of all installed packages that are currently
@@ -2507,7 +2636,7 @@
* @see #GET_UNINSTALLED_PACKAGES
*/
public abstract List<PackageInfo> getPackagesHoldingPermissions(
- String[] permissions, int flags);
+ String[] permissions, @PackageInfoFlags int flags);
/**
* Return a List of all packages that are installed on the device, for a specific user.
@@ -2546,7 +2675,7 @@
*
* @hide
*/
- public abstract List<PackageInfo> getInstalledPackages(int flags, int userId);
+ public abstract List<PackageInfo> getInstalledPackages(@PackageInfoFlags int flags, int userId);
/**
* Check whether a particular package has been granted a particular
@@ -2886,7 +3015,7 @@
* @see #GET_SHARED_LIBRARY_FILES
* @see #GET_UNINSTALLED_PACKAGES
*/
- public abstract List<ApplicationInfo> getInstalledApplications(int flags);
+ public abstract List<ApplicationInfo> getInstalledApplications(@ApplicationInfoFlags int flags);
/**
* Gets the ephemeral applications the user recently used. Requires
@@ -3021,7 +3150,7 @@
* @see #GET_INTENT_FILTERS
* @see #GET_RESOLVED_FILTER
*/
- public abstract ResolveInfo resolveActivity(Intent intent, int flags);
+ public abstract ResolveInfo resolveActivity(Intent intent, @ResolveInfoFlags int flags);
/**
* Determine the best action to perform for a given Intent for a given user. This
@@ -3054,7 +3183,8 @@
*
* @hide
*/
- public abstract ResolveInfo resolveActivityAsUser(Intent intent, int flags, int userId);
+ public abstract ResolveInfo resolveActivityAsUser(Intent intent, @ResolveInfoFlags int flags,
+ int userId);
/**
* Retrieve all activities that can be performed for the given intent.
@@ -3077,7 +3207,7 @@
* @see #GET_RESOLVED_FILTER
*/
public abstract List<ResolveInfo> queryIntentActivities(Intent intent,
- int flags);
+ @ResolveInfoFlags int flags);
/**
* Retrieve all activities that can be performed for the given intent, for a specific user.
@@ -3101,8 +3231,7 @@
* @hide
*/
public abstract List<ResolveInfo> queryIntentActivitiesAsUser(Intent intent,
- int flags, int userId);
-
+ @ResolveInfoFlags int flags, int userId);
/**
* Retrieve a set of activities that should be presented to the user as
@@ -3134,7 +3263,7 @@
* @see #GET_RESOLVED_FILTER
*/
public abstract List<ResolveInfo> queryIntentActivityOptions(
- ComponentName caller, Intent[] specifics, Intent intent, int flags);
+ ComponentName caller, Intent[] specifics, Intent intent, @ResolveInfoFlags int flags);
/**
* Retrieve all receivers that can handle a broadcast of the given intent.
@@ -3151,7 +3280,7 @@
* @see #GET_RESOLVED_FILTER
*/
public abstract List<ResolveInfo> queryBroadcastReceivers(Intent intent,
- int flags);
+ @ResolveInfoFlags int flags);
/**
* Retrieve all receivers that can handle a broadcast of the given intent, for a specific
@@ -3171,7 +3300,7 @@
* @hide
*/
public abstract List<ResolveInfo> queryBroadcastReceivers(Intent intent,
- int flags, int userId);
+ @ResolveInfoFlags int flags, int userId);
/**
* Determine the best service to handle for a given Intent.
@@ -3187,7 +3316,7 @@
* @see #GET_INTENT_FILTERS
* @see #GET_RESOLVED_FILTER
*/
- public abstract ResolveInfo resolveService(Intent intent, int flags);
+ public abstract ResolveInfo resolveService(Intent intent, @ResolveInfoFlags int flags);
/**
* Retrieve all services that can match the given intent.
@@ -3205,7 +3334,7 @@
* @see #GET_RESOLVED_FILTER
*/
public abstract List<ResolveInfo> queryIntentServices(Intent intent,
- int flags);
+ @ResolveInfoFlags int flags);
/**
* Retrieve all services that can match the given intent for a given user.
@@ -3226,11 +3355,11 @@
* @hide
*/
public abstract List<ResolveInfo> queryIntentServicesAsUser(Intent intent,
- int flags, int userId);
+ @ResolveInfoFlags int flags, int userId);
/** {@hide} */
public abstract List<ResolveInfo> queryIntentContentProvidersAsUser(
- Intent intent, int flags, int userId);
+ Intent intent, @ResolveInfoFlags int flags, int userId);
/**
* Retrieve all providers that can match the given intent.
@@ -3244,7 +3373,8 @@
* @see #GET_INTENT_FILTERS
* @see #GET_RESOLVED_FILTER
*/
- public abstract List<ResolveInfo> queryIntentContentProviders(Intent intent, int flags);
+ public abstract List<ResolveInfo> queryIntentContentProviders(Intent intent,
+ @ResolveInfoFlags int flags);
/**
* Find a single content provider by its base path name.
@@ -3256,7 +3386,7 @@
* else null.
*/
public abstract ProviderInfo resolveContentProvider(String name,
- int flags);
+ @ComponentInfoFlags int flags);
/**
* Find a single content provider by its base path name.
@@ -3269,7 +3399,8 @@
* else null.
* @hide
*/
- public abstract ProviderInfo resolveContentProviderAsUser(String name, int flags, int userId);
+ public abstract ProviderInfo resolveContentProviderAsUser(String name,
+ @ComponentInfoFlags int flags, int userId);
/**
* Retrieve content provider information.
@@ -3290,7 +3421,7 @@
* <em>If there are no matching providers, null is returned.</em>
*/
public abstract List<ProviderInfo> queryContentProviders(
- String processName, int uid, int flags);
+ String processName, int uid, @ComponentInfoFlags int flags);
/**
* Retrieve all of the information we know about a particular
@@ -3307,8 +3438,8 @@
* @return InstrumentationInfo containing information about the
* instrumentation.
*/
- public abstract InstrumentationInfo getInstrumentationInfo(
- ComponentName className, int flags) throws NameNotFoundException;
+ public abstract InstrumentationInfo getInstrumentationInfo(ComponentName className,
+ @InstrumentationInfoFlags int flags) throws NameNotFoundException;
/**
* Retrieve information about available instrumentation code. May be used
@@ -3324,8 +3455,8 @@
* matching available Instrumentation. Returns an empty list if
* there is no instrumentation available for the given package.
*/
- public abstract List<InstrumentationInfo> queryInstrumentation(
- String targetPackage, int flags);
+ public abstract List<InstrumentationInfo> queryInstrumentation(String targetPackage,
+ @InstrumentationInfoFlags int flags);
/**
* Retrieve an image from a package. This is a low-level API used by
@@ -3761,7 +3892,7 @@
* @see #GET_SIGNATURES
*
*/
- public PackageInfo getPackageArchiveInfo(String archiveFilePath, int flags) {
+ public PackageInfo getPackageArchiveInfo(String archiveFilePath, @PackageInfoFlags int flags) {
final PackageParser parser = new PackageParser();
final File apkFile = new File(archiveFilePath);
try {
@@ -4423,7 +4554,7 @@
* @see #GET_SERVICES
* @see #GET_SIGNATURES
*/
- public abstract List<PackageInfo> getPreferredPackages(int flags);
+ public abstract List<PackageInfo> getPreferredPackages(@PackageInfoFlags int flags);
/**
* @deprecated This is a protected API that should not have been available
diff --git a/core/java/android/content/pm/ParceledListSlice.java b/core/java/android/content/pm/ParceledListSlice.java
index cfb4473..945858e6 100644
--- a/core/java/android/content/pm/ParceledListSlice.java
+++ b/core/java/android/content/pm/ParceledListSlice.java
@@ -24,6 +24,7 @@
import android.util.Log;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
/**
@@ -50,6 +51,10 @@
private final List<T> mList;
+ public static <T extends Parcelable> ParceledListSlice<T> emptyList() {
+ return new ParceledListSlice<T>(Collections.<T> emptyList());
+ }
+
public ParceledListSlice(List<T> list) {
mList = list;
}
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/notification/INotificationAssistant.aidl b/core/java/android/service/notification/INotificationAssistant.aidl
deleted file mode 100644
index 5c5f358..0000000
--- a/core/java/android/service/notification/INotificationAssistant.aidl
+++ /dev/null
@@ -1,37 +0,0 @@
-/**
- * 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.service.notification;
-
-import android.service.notification.NotificationAdjustment;
-import android.service.notification.IStatusBarNotificationHolder;
-import android.service.notification.NotificationRankingUpdate;
-
-/** @hide */
-interface INotificationAssistant
-{
- void onListenerConnected(in NotificationRankingUpdate update);
- void onNotificationPosted(in IStatusBarNotificationHolder notificationHolder,
- in NotificationRankingUpdate update);
- void onNotificationRankingUpdate(in NotificationRankingUpdate update);
- void onListenerHintsChanged(int hints);
- void onInterruptionFilterChanged(int interruptionFilter);
- NotificationAdjustment onNotificationEnqueued(in IStatusBarNotificationHolder notificationHolder, int importance, boolean user);
- void onNotificationVisibilityChanged(String key, long time, boolean visible);
- void onNotificationClick(String key, long time);
- void onNotificationActionClick(String key, long time, int actionIndex);
- void onNotificationRemoved(String key, long time, int reason);
-}
\ No newline at end of file
diff --git a/core/java/android/service/notification/INotificationListener.aidl b/core/java/android/service/notification/INotificationListener.aidl
index e6bf6ba..a0de17f 100644
--- a/core/java/android/service/notification/INotificationListener.aidl
+++ b/core/java/android/service/notification/INotificationListener.aidl
@@ -23,6 +23,7 @@
/** @hide */
oneway interface INotificationListener
{
+ // listeners and assistants
void onListenerConnected(in NotificationRankingUpdate update);
void onNotificationPosted(in IStatusBarNotificationHolder notificationHolder,
in NotificationRankingUpdate update);
@@ -31,4 +32,11 @@
void onNotificationRankingUpdate(in NotificationRankingUpdate update);
void onListenerHintsChanged(int hints);
void onInterruptionFilterChanged(int interruptionFilter);
+
+ // assistants only
+ void onNotificationEnqueued(in IStatusBarNotificationHolder notificationHolder, int importance, boolean user);
+ void onNotificationVisibilityChanged(String key, long time, boolean visible);
+ void onNotificationClick(String key, long time);
+ void onNotificationActionClick(String key, long time, int actionIndex);
+ void onNotificationRemovedReason(String key, long time, int reason);
}
diff --git a/core/java/android/service/notification/NotificationAdjustment.java b/core/java/android/service/notification/NotificationAdjustment.java
deleted file mode 100644
index c5f0db9..0000000
--- a/core/java/android/service/notification/NotificationAdjustment.java
+++ /dev/null
@@ -1,57 +0,0 @@
-package android.service.notification;
-
-import android.net.Uri;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-public class NotificationAdjustment implements Parcelable {
- int mImportance;
- CharSequence mExplanation;
- Uri mReference;
-
- /**
- * Create a notification importance adjustment.
- *
- * @param importance The final importance of the notification.
- * @param explanation A human-readable justification for the adjustment.
- * @param reference A reference to an external object that augments the
- * explanation, such as a
- * {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI},
- * or null.
- */
- public NotificationAdjustment(int importance, CharSequence explanation, Uri reference) {
- mImportance = importance;
- mExplanation = explanation;
- mReference = reference;
- }
-
- private NotificationAdjustment(Parcel source) {
- this(source.readInt(), source.readCharSequence(),
- (Uri) source.readParcelable(NotificationAdjustment.class.getClassLoader()));
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeInt(mImportance);
- dest.writeCharSequence(mExplanation);
- dest.writeParcelable(mReference, 0);
- }
-
- public static final Parcelable.Creator<NotificationAdjustment> CREATOR
- = new Parcelable.Creator<NotificationAdjustment>() {
- @Override
- public NotificationAdjustment createFromParcel(Parcel source) {
- return new NotificationAdjustment(source);
- }
-
- @Override
- public NotificationAdjustment[] newArray(int size) {
- return new NotificationAdjustment[size];
- }
- };
-}
diff --git a/core/java/android/service/notification/NotificationAssistantService.java b/core/java/android/service/notification/NotificationAssistantService.java
index 7ce5e1f..3a8956e 100644
--- a/core/java/android/service/notification/NotificationAssistantService.java
+++ b/core/java/android/service/notification/NotificationAssistantService.java
@@ -20,8 +20,11 @@
import android.app.Notification;
import android.content.Intent;
import android.net.Uri;
+import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcelable;
+import android.os.RemoteException;
+import android.util.Log;
/**
* A service that helps the user manage notifications by modifying the
@@ -39,6 +42,8 @@
* </service></pre>
*/
public abstract class NotificationAssistantService extends NotificationListenerService {
+ private static final String TAG = "NotificationAssistant";
+
/**
* The {@link Intent} that must be declared as handled by the service.
*/
@@ -85,6 +90,36 @@
/** Notification was canceled because it was an invisible member of a group. */
public static final int REASON_GROUP_OPTIMIZATION = 13;
+ public class Adjustment {
+ int mImportance;
+ CharSequence mExplanation;
+ Uri mReference;
+
+ /**
+ * Create a notification importance adjustment.
+ *
+ * @param importance The final importance of the notification.
+ * @param explanation A human-readable justification for the adjustment.
+ * @param reference A reference to an external object that augments the
+ * explanation, such as a
+ * {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI},
+ * or null.
+ */
+ public Adjustment(int importance, CharSequence explanation, Uri reference) {
+ mImportance = importance;
+ mExplanation = explanation;
+ mReference = reference;
+ }
+ }
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ if (mWrapper == null) {
+ mWrapper = new NotificationAssistantWrapper();
+ }
+ return mWrapper;
+ }
+
/**
* A notification was posted by an app. Called before alert.
*
@@ -93,7 +128,7 @@
* @param user true if the initial importance reflects an explicit user preference.
* @return an adjustment or null to take no action, within 100ms.
*/
- abstract public NotificationAdjustment onNotificationEnqueued(StatusBarNotification sbn,
+ abstract public Adjustment onNotificationEnqueued(StatusBarNotification sbn,
int importance, boolean user);
/**
@@ -150,9 +185,15 @@
* @param key the notification key
* @param adjustment the new importance with an explanation
*/
- public final void adjustImportance(String key, NotificationAdjustment adjustment)
+ public final void adjustImportance(String key, Adjustment adjustment)
{
- // TODO: pack up the adjustment and send it to the NotificationManager.
+ if (!isBound()) return;
+ try {
+ getNotificationInterface().setImportanceFromAssistant(mWrapper, key,
+ adjustment.mImportance, adjustment.mExplanation);
+ } catch (android.os.RemoteException ex) {
+ Log.v(TAG, "Unable to contact notification manager", ex);
+ }
}
/**
@@ -160,7 +201,7 @@
* be fired when the host notification is deleted, or when this annotation
* is removed or replaced.
*
- * @param key the notification key
+ * @param key the key of the notification to be annotated
* @param annotation the new annotation object
*/
public final void setAnnotation(String key, Notification annotation)
@@ -171,10 +212,74 @@
/**
* Remove the annotation from a notification.
*
- * @param key the notification key
+ * @param key the key of the notification to be cleansed of annotatons
*/
public final void clearAnnotation(String key)
{
// TODO: ask the NotificationManager to clear the annotation.
}
+
+ private class NotificationAssistantWrapper extends NotificationListenerWrapper {
+ @Override
+ public void onNotificationEnqueued(IStatusBarNotificationHolder sbnHolder,
+ int importance, boolean user) throws RemoteException {
+ StatusBarNotification sbn;
+ try {
+ sbn = sbnHolder.get();
+ } catch (RemoteException e) {
+ Log.w(TAG, "onNotificationEnqueued: Error receiving StatusBarNotification", e);
+ return;
+ }
+
+ try {
+ Adjustment adjustment =
+ NotificationAssistantService.this.onNotificationEnqueued(sbn, importance, user);
+ if (adjustment != null) {
+ adjustImportance(sbn.getKey(), adjustment);
+ }
+ } catch (Throwable t) {
+ Log.w(TAG, "Error running onNotificationEnqueued", t);
+ }
+ }
+
+ @Override
+ public void onNotificationVisibilityChanged(String key, long time, boolean visible)
+ throws RemoteException {
+ try {
+ NotificationAssistantService.this.onNotificationVisibilityChanged(key, time,
+ visible);
+ } catch (Throwable t) {
+ Log.w(TAG, "Error running onNotificationVisibilityChanged", t);
+ }
+ }
+
+ @Override
+ public void onNotificationClick(String key, long time) throws RemoteException {
+ try {
+ NotificationAssistantService.this.onNotificationClick(key, time);
+ } catch (Throwable t) {
+ Log.w(TAG, "Error running onNotificationClick", t);
+ }
+ }
+
+ @Override
+ public void onNotificationActionClick(String key, long time, int actionIndex)
+ throws RemoteException {
+ try {
+ NotificationAssistantService.this.onNotificationActionClick(key, time, actionIndex);
+ } catch (Throwable t) {
+ Log.w(TAG, "Error running onNotificationActionClick", t);
+ }
+ }
+
+ @Override
+ public void onNotificationRemovedReason(String key, long time, int reason)
+ throws RemoteException {
+ try {
+ NotificationAssistantService.this.onNotificationRemoved(key, time, reason);
+ } catch (Throwable t) {
+ Log.w(TAG, "Error running onNotificationRemoved", t);
+ }
+ }
+ }
}
diff --git a/core/java/android/service/notification/NotificationListenerService.java b/core/java/android/service/notification/NotificationListenerService.java
index 1e62edc..6c99489 100644
--- a/core/java/android/service/notification/NotificationListenerService.java
+++ b/core/java/android/service/notification/NotificationListenerService.java
@@ -15,6 +15,7 @@
*/
package android.service.notification;
+import android.service.notification.IStatusBarNotificationHolder;
import android.annotation.SystemApi;
import android.annotation.SdkConstant;
@@ -151,7 +152,8 @@
@SystemApi
public static final int TRIM_LIGHT = 1;
- private INotificationListenerWrapper mWrapper = null;
+ /** @hide */
+ protected NotificationListenerWrapper mWrapper = null;
private RankingMap mRankingMap;
private INotificationManager mNoMan;
@@ -291,7 +293,8 @@
// optional
}
- private final INotificationManager getNotificationInterface() {
+ /** @hide */
+ protected final INotificationManager getNotificationInterface() {
if (mNoMan == null) {
mNoMan = INotificationManager.Stub.asInterface(
ServiceManager.getService(Context.NOTIFICATION_SERVICE));
@@ -634,12 +637,13 @@
@Override
public IBinder onBind(Intent intent) {
if (mWrapper == null) {
- mWrapper = new INotificationListenerWrapper();
+ mWrapper = new NotificationListenerWrapper();
}
return mWrapper;
}
- private boolean isBound() {
+ /** @hide */
+ protected boolean isBound() {
if (mWrapper == null) {
Log.w(TAG, "Notification listener service not yet bound.");
return false;
@@ -664,7 +668,7 @@
int currentUser) throws RemoteException {
mSystemContext = context;
if (mWrapper == null) {
- mWrapper = new INotificationListenerWrapper();
+ mWrapper = new NotificationListenerWrapper();
}
INotificationManager noMan = getNotificationInterface();
noMan.registerListener(mWrapper, componentName, currentUser);
@@ -716,7 +720,8 @@
}
}
- private class INotificationListenerWrapper extends INotificationListener.Stub {
+ /** @hide */
+ protected class NotificationListenerWrapper extends INotificationListener.Stub {
@Override
public void onNotificationPosted(IStatusBarNotificationHolder sbnHolder,
NotificationRankingUpdate update) {
@@ -817,6 +822,35 @@
Log.w(TAG, "Error running onInterruptionFilterChanged", t);
}
}
+
+ @Override
+ public void onNotificationEnqueued(IStatusBarNotificationHolder notificationHolder,
+ int importance, boolean user) throws RemoteException {
+ // no-op in the listener
+ }
+
+ @Override
+ public void onNotificationVisibilityChanged(String key, long time, boolean visible)
+ throws RemoteException {
+ // no-op in the listener
+ }
+
+ @Override
+ public void onNotificationClick(String key, long time) throws RemoteException {
+ // no-op in the listener
+ }
+
+ @Override
+ public void onNotificationActionClick(String key, long time, int actionIndex)
+ throws RemoteException {
+ // no-op in the listener
+ }
+
+ @Override
+ public void onNotificationRemovedReason(String key, long time, int reason)
+ throws RemoteException {
+ // no-op in the listener
+ }
}
private void applyUpdate(NotificationRankingUpdate update) {
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..37af59f 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -107,6 +107,7 @@
<protected-broadcast android:name="android.backup.intent.CLEAR" />
<protected-broadcast android:name="android.backup.intent.INIT" />
+ <protected-broadcast android:name="android.bluetooth.intent.DISCOVERABLE_TIMEOUT" />
<protected-broadcast android:name="android.bluetooth.adapter.action.STATE_CHANGED" />
<protected-broadcast android:name="android.bluetooth.adapter.action.SCAN_MODE_CHANGED" />
<protected-broadcast android:name="android.bluetooth.adapter.action.DISCOVERY_STARTED" />
@@ -1901,7 +1902,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/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index 5177836..c0453f8 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -146,6 +146,16 @@
</intent-filter>
</activity>
+ <activity android:name="android.widget.DatePickerActivity"
+ android:label="DatePickerActivity"
+ android:screenOrientation="portrait"
+ android:theme="@android:style/Theme.Material.Light">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
+ </intent-filter>
+ </activity>
+
<activity android:name="android.widget.focus.DescendantFocusability" android:label="DescendantFocusability">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
diff --git a/core/tests/coretests/res/layout/datepicker_layout.xml b/core/tests/coretests/res/layout/datepicker_layout.xml
new file mode 100644
index 0000000..a79f87d
--- /dev/null
+++ b/core/tests/coretests/res/layout/datepicker_layout.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<RelativeLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <DatePicker
+ android:id="@+id/datePicker"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_centerInParent="true"/>
+ <EditText
+ android:id="@+id/belowPicker"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="Some Text"
+ android:textAlignment="center"
+ android:layout_below="@id/datePicker"/>
+</RelativeLayout>
diff --git a/core/tests/coretests/src/android/widget/DatePickerActivity.java b/core/tests/coretests/src/android/widget/DatePickerActivity.java
new file mode 100644
index 0000000..c3b25a1
--- /dev/null
+++ b/core/tests/coretests/src/android/widget/DatePickerActivity.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2008 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.widget;
+
+import android.app.Activity;
+import android.os.Bundle;
+import com.android.frameworks.coretests.R;
+
+/**
+ * A minimal application for DatePickerFocusTest.
+ */
+public class DatePickerActivity extends Activity {
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.datepicker_layout);
+ }
+}
diff --git a/core/tests/coretests/src/android/widget/DatePickerFocusTest.java b/core/tests/coretests/src/android/widget/DatePickerFocusTest.java
new file mode 100644
index 0000000..513e40f
--- /dev/null
+++ b/core/tests/coretests/src/android/widget/DatePickerFocusTest.java
@@ -0,0 +1,250 @@
+/*
+ * Copyright (C) 2008 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.widget;
+
+import android.app.Activity;
+import android.app.Instrumentation;
+import android.os.SystemClock;
+import android.test.ActivityInstrumentationTestCase2;
+import android.view.KeyEvent;
+import android.view.View;
+
+import com.android.frameworks.coretests.R;
+
+/**
+ * Test {@link DatePicker} focus changes.
+ */
+public class DatePickerFocusTest extends ActivityInstrumentationTestCase2<DatePickerActivity> {
+
+ private Activity mActivity;
+ private Instrumentation mInstrumentation;
+ private DatePicker mDatePicker;
+
+ public DatePickerFocusTest() {
+ super(DatePickerActivity.class);
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ mActivity = getActivity();
+ mInstrumentation = getInstrumentation();
+
+ mDatePicker = (DatePicker) mActivity.findViewById(R.id.datePicker);
+ }
+
+ /**
+ * Tabs (forward and backward) through the DatePicker to ensure the correct
+ * Views gain focus.
+ */
+ public void testFocusTravel() throws Throwable {
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ assertTrue(mDatePicker.requestFocus());
+ }
+ });
+ assertViewHasFocus(com.android.internal.R.id.date_picker_header_year);
+ sendKey(KeyEvent.KEYCODE_TAB);
+ assertViewHasFocus(com.android.internal.R.id.prev);
+ sendKey(KeyEvent.KEYCODE_TAB);
+ assertViewHasFocus(com.android.internal.R.id.next);
+ sendKey(KeyEvent.KEYCODE_TAB);
+ assertViewHasFocus(com.android.internal.R.id.day_picker_view_pager);
+ sendKey(KeyEvent.KEYCODE_TAB);
+ assertViewHasFocus(R.id.belowPicker);
+ sendShiftKey(KeyEvent.KEYCODE_TAB);
+ assertViewHasFocus(com.android.internal.R.id.day_picker_view_pager);
+ sendShiftKey(KeyEvent.KEYCODE_TAB);
+ assertViewHasFocus(com.android.internal.R.id.next);
+ sendShiftKey(KeyEvent.KEYCODE_TAB);
+ assertViewHasFocus(com.android.internal.R.id.prev);
+ sendShiftKey(KeyEvent.KEYCODE_TAB);
+ assertViewHasFocus(com.android.internal.R.id.date_picker_header_year);
+ }
+
+ private void sendKey(int keycode) {
+ mInstrumentation.sendKeyDownUpSync(keycode);
+ mInstrumentation.waitForIdleSync();
+ }
+
+ private void assertViewHasFocus(final int id) throws Throwable {
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ View view = mActivity.findViewById(id);
+ assertTrue(view.hasFocus());
+ }
+ });
+ }
+
+ private void sendShiftKey(int keycode) {
+ final KeyEvent shiftDown = new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_SHIFT_LEFT);
+ mInstrumentation.sendKeySync(shiftDown);
+
+ final KeyEvent keyDown = new KeyEvent(SystemClock.uptimeMillis(),
+ SystemClock.uptimeMillis(), KeyEvent.ACTION_DOWN, keycode, 0,
+ KeyEvent.META_SHIFT_ON);
+ mInstrumentation.sendKeySync(keyDown);
+
+ final KeyEvent keyUp = new KeyEvent(SystemClock.uptimeMillis(),
+ SystemClock.uptimeMillis(), KeyEvent.ACTION_UP, keycode, 0,
+ KeyEvent.META_SHIFT_ON);
+ mInstrumentation.sendKeySync(keyUp);
+
+ final KeyEvent shiftUp = new KeyEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_SHIFT_LEFT);
+ mInstrumentation.sendKeySync(shiftUp);
+
+ mInstrumentation.waitForIdleSync();
+ }
+
+ /**
+ * Tests to ensure the keyboard can select the current year.
+ */
+ public void testYearChoice() throws Throwable {
+ setKnownDate();
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ View year = mDatePicker.
+ findViewById(com.android.internal.R.id.date_picker_header_year);
+ assertTrue(year.requestFocus());
+ }
+ });
+ sendKey(KeyEvent.KEYCODE_ENTER);
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ View yearSelect = mDatePicker.
+ findViewById(com.android.internal.R.id.date_picker_year_picker);
+ assertEquals(yearSelect, mDatePicker.findFocus());
+ }
+ });
+ sendKey(KeyEvent.KEYCODE_DPAD_UP);
+ sendKey(KeyEvent.KEYCODE_ENTER);
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ View yearSelect = mDatePicker.
+ findViewById(com.android.internal.R.id.date_picker_year_picker);
+ assertNotSame(View.VISIBLE, yearSelect.getVisibility());
+ View year = mDatePicker.
+ findViewById(com.android.internal.R.id.date_picker_header_year);
+ assertTrue(year.hasFocus());
+ assertEquals(2014, mDatePicker.getYear());
+ }
+ });
+ }
+
+ public void testArrowThroughDays() throws Throwable {
+ setKnownDate();
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ View prev = mDatePicker.findViewById(com.android.internal.R.id.next);
+ prev.requestFocus();
+ }
+ });
+ sendKey(KeyEvent.KEYCODE_TAB);
+ // Should select the current date and the date shouldn't change
+ sendKey(KeyEvent.KEYCODE_ENTER);
+ assertDateIs(12, 31, 2015);
+ // Move right to January 24, 2016
+ sendKey(KeyEvent.KEYCODE_DPAD_RIGHT);
+ sendKey(KeyEvent.KEYCODE_ENTER);
+ assertDateIs(1, 24, 2016);
+ // Move down to January 31, 2016
+ sendKey(KeyEvent.KEYCODE_DPAD_DOWN);
+ sendKey(KeyEvent.KEYCODE_ENTER);
+ assertDateIs(1, 31, 2016);
+ // Move up to January 5, 2016
+ sendKey(KeyEvent.KEYCODE_DPAD_UP);
+ sendKey(KeyEvent.KEYCODE_DPAD_UP);
+ sendKey(KeyEvent.KEYCODE_DPAD_UP);
+ sendKey(KeyEvent.KEYCODE_DPAD_UP);
+ sendKey(KeyEvent.KEYCODE_DPAD_RIGHT);
+ sendKey(KeyEvent.KEYCODE_DPAD_RIGHT);
+ sendKey(KeyEvent.KEYCODE_ENTER);
+ assertDateIs(1, 5, 2016);
+ // Move up to prev arrow key
+ sendKey(KeyEvent.KEYCODE_DPAD_UP);
+ assertViewHasFocus(com.android.internal.R.id.prev);
+ // tab back into the day-selection pager
+ sendKey(KeyEvent.KEYCODE_TAB);
+ sendKey(KeyEvent.KEYCODE_TAB);
+ sendKey(KeyEvent.KEYCODE_ENTER);
+ assertViewHasFocus(com.android.internal.R.id.day_picker_view_pager);
+ assertDateIs(1, 5, 2016);
+
+ // Move up out again, then down back into the day-selection pager.
+ // It should land right below the prev button (1/3/2016)
+ sendKey(KeyEvent.KEYCODE_DPAD_UP);
+ sendKey(KeyEvent.KEYCODE_DPAD_DOWN);
+ sendKey(KeyEvent.KEYCODE_ENTER);
+ assertViewHasFocus(com.android.internal.R.id.day_picker_view_pager);
+ assertDateIs(1, 3, 2016);
+
+ // Move left to previous month (12/12/2015)
+ sendKey(KeyEvent.KEYCODE_DPAD_LEFT);
+ sendKey(KeyEvent.KEYCODE_ENTER);
+ assertDateIs(12, 12, 2015);
+ // Now make sure the start of the month works
+ // Move up to 12/5/2015 and right to 1/1/2016
+ sendKey(KeyEvent.KEYCODE_DPAD_UP);
+ sendKey(KeyEvent.KEYCODE_DPAD_RIGHT);
+ sendKey(KeyEvent.KEYCODE_ENTER);
+ assertDateIs(1, 1, 2016);
+ // Now make sure the left key goes back to previous month (12/5/2015)
+ sendKey(KeyEvent.KEYCODE_DPAD_LEFT);
+ sendKey(KeyEvent.KEYCODE_ENTER);
+ assertDateIs(12, 5, 2015);
+ // Now go to a mismatched row (no such row on previous month)
+ // This moves over to 1/31/2016 and then left to 12/31/2015
+ sendKey(KeyEvent.KEYCODE_DPAD_DOWN);
+ sendKey(KeyEvent.KEYCODE_DPAD_DOWN);
+ sendKey(KeyEvent.KEYCODE_DPAD_DOWN);
+ sendKey(KeyEvent.KEYCODE_DPAD_RIGHT);
+ sendKey(KeyEvent.KEYCODE_DPAD_DOWN);
+ sendKey(KeyEvent.KEYCODE_DPAD_DOWN);
+ sendKey(KeyEvent.KEYCODE_DPAD_LEFT);
+ sendKey(KeyEvent.KEYCODE_ENTER);
+ assertDateIs(12, 31, 2015);
+ }
+
+ private void assertDateIs(int month, final int day, final int year) throws Throwable {
+ final int monthInt = month - 1; // months are 0-based
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ assertEquals(day, mDatePicker.getDayOfMonth());
+ assertEquals(year, mDatePicker.getYear());
+ assertEquals(monthInt, mDatePicker.getMonth());
+ }
+ });
+ }
+
+ private void setKnownDate() throws Throwable {
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ mDatePicker.updateDate(2015, 11, 31); // December 31, 2015
+ }
+ });
+ mInstrumentation.waitForIdleSync();
+ }
+}
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/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/notification_guts.xml b/packages/SystemUI/res/layout/notification_guts.xml
index 3cdee64..071b7c8 100644
--- a/packages/SystemUI/res/layout/notification_guts.xml
+++ b/packages/SystemUI/res/layout/notification_guts.xml
@@ -29,25 +29,22 @@
android:background="@color/notification_guts_text_color" >
<!-- header -->
- <FrameLayout
+ <LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:paddingBottom="8dp" >
+ android:paddingBottom="8dp"
+ android:paddingTop="8dp"
+ android:id="@+id/notification_guts_header"
+ android:orientation="horizontal"
+ android:layout_gravity="center_vertical|start">
- <LinearLayout
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:id="@+id/notification_guts_header"
- android:orientation="horizontal"
- android:layout_gravity="center_vertical|start"
- android:layout_marginEnd="52dp">
- <ImageView
- android:id="@android:id/icon"
- android:layout_width="18dp"
- android:layout_height="18dp"
- android:layout_marginEnd="3dp"
- android:src="@android:drawable/arrow_down_float" />
- <TextView
+ <ImageView
+ android:id="@android:id/icon"
+ android:layout_width="18dp"
+ android:layout_height="18dp"
+ android:layout_marginEnd="3dp"
+ android:src="@android:drawable/arrow_down_float" />
+ <TextView
android:id="@+id/pkgname"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -55,7 +52,7 @@
android:layout_marginStart="3dp"
android:layout_marginEnd="4dp"
android:textColor="@color/notification_guts_title_color" />
- <TextView
+ <TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/debug_info"
@@ -64,18 +61,7 @@
android:layout_gravity="bottom|start"
android:visibility="gone"
android:textColor="#ffffff" />
- </LinearLayout>
-
- <ImageButton style="@android:style/Widget.Material.Light.Button.Borderless.Small"
- android:id="@+id/notification_inspect_item"
- android:layout_width="52dp"
- android:layout_height="wrap_content"
- android:layout_weight="0"
- android:gravity="center"
- android:layout_gravity="center_vertical|end"
- android:contentDescription="@string/status_bar_notification_inspect_item_title"
- android:src="@drawable/ic_settings" />
- </FrameLayout>
+ </LinearLayout>
<!-- Importance slider -->
<LinearLayout
android:layout_width="match_parent"
@@ -157,4 +143,34 @@
android:visibility="gone"/>
</RadioGroup>
</LinearLayout>
+ <!-- buttons -->
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="end"
+ android:paddingTop="8dp"
+ android:paddingBottom="16dp" >
+
+ <TextView
+ android:id="@+id/more_settings"
+ android:text="@string/notification_more_settings"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="@style/TextAppearance.NotificationGuts"
+ android:background="@drawable/btn_borderless_rect"
+ android:gravity="center"
+ android:paddingEnd="24dp"
+ android:paddingStart="12dp"
+ android:focusable="true" />
+
+ <TextView
+ android:id="@+id/done"
+ android:text="@string/notification_done"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="@style/TextAppearance.NotificationGuts"
+ android:background="@drawable/btn_borderless_rect"
+ android:gravity="center"
+ android:focusable="true"/>
+ </LinearLayout>
</com.android.systemui.statusbar.NotificationGuts>
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/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 45ddd50..876c21e 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -1228,6 +1228,11 @@
<!-- [CHAR LIMIT=100] Notification Importance slider: max importance level description -->
<string name="notification_importance_max">Peek onto the screen and make sound</string>
+ <!-- Notification: Control panel: Label for button that launches notification settings. [CHAR LIMIT=NONE] -->
+ <string name="notification_more_settings">More settings</string>
+ <!-- Notification: Control panel: Label for button that dismisses control panel. [CHAR LIMIT=NONE] -->
+ <string name="notification_done">Done</string>
+
<!-- Label for no color transform [CHAR LIMIT=30] -->
<string name="color_matrix_none">Normal colors</string>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 3b0ab791..527b638 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -317,4 +317,12 @@
<item name="@dropdownPreferenceStyle">@style/Preference.DropDown.Material</item>
</style>
+ <style name="TextAppearance.NotificationGuts">
+ <item name="android:textSize">14sp</item>
+ <item name="android:textColor">@color/notification_guts_btn_color</item>
+ <item name="android:textAllCaps">true</item>
+ <item name="android:fontFamily">sans-serif-medium</item>
+ <item name="android:gravity">center</item>
+ </style>
+
</resources>
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/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index 1d7651c..11d7b9b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -970,7 +970,7 @@
((ImageView) row.findViewById(android.R.id.icon)).setImageDrawable(pkgicon);
((TextView) row.findViewById(R.id.pkgname)).setText(appname);
- final View settingsButton = guts.findViewById(R.id.notification_inspect_item);
+ final View settingsButton = guts.findViewById(R.id.more_settings);
if (appUid >= 0) {
final int appUidF = appUid;
settingsButton.setOnClickListener(new View.OnClickListener() {
@@ -983,6 +983,13 @@
settingsButton.setVisibility(View.GONE);
}
+ row.findViewById(R.id.done).setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ dismissPopups();
+ }
+ });
+
guts.bindImportance(sbn, row, mNotificationData.getImportance(sbn.getKey()));
}
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..1c25067 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();
}
}
}
@@ -3590,7 +3602,7 @@
Intent getHomeIntent() {
Intent intent = new Intent(mTopAction, mTopData != null ? Uri.parse(mTopData) : null);
intent.setComponent(mTopComponent);
- intent.addFlags(Intent.FLAG_DEBUG_ENCRYPTION_TRIAGED);
+ intent.addFlags(Intent.FLAG_DEBUG_TRIAGED_MISSING);
if (mFactoryTest != FactoryTest.FACTORY_TEST_LOW_LEVEL) {
intent.addCategory(Intent.CATEGORY_HOME);
}
@@ -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();
}
}
}
@@ -9802,7 +9813,7 @@
ParceledListSlice<ProviderInfo> slice = AppGlobals.getPackageManager()
.queryContentProviders(app.processName, app.uid,
STOCK_PM_FLAGS | PackageManager.GET_URI_PERMISSION_PATTERNS
- | PackageManager.MATCH_ENCRYPTION_DEFAULT);
+ | PackageManager.MATCH_DEBUG_TRIAGED_MISSING);
providers = slice != null ? slice.getList() : null;
} catch (RemoteException ex) {
}
@@ -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);
}
@@ -17010,7 +17021,7 @@
private List<ResolveInfo> collectReceiverComponents(Intent intent, String resolvedType,
int callingUid, int[] users) {
// TODO: come back and remove this assumption to triage all broadcasts
- int pmFlags = STOCK_PM_FLAGS | PackageManager.MATCH_ENCRYPTION_DEFAULT;
+ int pmFlags = STOCK_PM_FLAGS | PackageManager.MATCH_DEBUG_TRIAGED_MISSING;
List<ResolveInfo> receivers = null;
try {
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..f2c5206 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -304,7 +304,10 @@
+ relatedUserId);
// We still need to stop the requested user if it's a force stop.
if (force) {
+ Slog.i(TAG,
+ "Force stop user " + userId + ". Related users will not be stopped");
stopSingleUserLocked(userId, callback);
+ return USER_OP_SUCCESS;
}
return USER_OP_ERROR_RELATED_USERS_CANNOT_STOP;
}
@@ -931,7 +934,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/job/JobSchedulerService.java b/services/core/java/com/android/server/job/JobSchedulerService.java
index c5e6e7c..4eabe36 100644
--- a/services/core/java/com/android/server/job/JobSchedulerService.java
+++ b/services/core/java/com/android/server/job/JobSchedulerService.java
@@ -161,8 +161,9 @@
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
- Slog.d(TAG, "Receieved: " + intent.getAction());
- if (Intent.ACTION_PACKAGE_REMOVED.equals(intent.getAction())) {
+ final String action = intent.getAction();
+ Slog.d(TAG, "Receieved: " + action);
+ if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
// If this is an outright uninstall rather than the first half of an
// app update sequence, cancel the jobs associated with the app.
if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
@@ -172,18 +173,21 @@
}
cancelJobsForUid(uidRemoved, true);
}
- } else if (Intent.ACTION_USER_REMOVED.equals(intent.getAction())) {
+ } else if (Intent.ACTION_USER_REMOVED.equals(action)) {
final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0);
if (DEBUG) {
Slog.d(TAG, "Removing jobs for user: " + userId);
}
cancelJobsForUser(userId);
- } else if (PowerManager.ACTION_LIGHT_DEVICE_IDLE_MODE_CHANGED.equals(intent.getAction())
- || PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED.equals(intent.getAction())) {
+ } else if (PowerManager.ACTION_LIGHT_DEVICE_IDLE_MODE_CHANGED.equals(action)
+ || PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED.equals(action)) {
updateIdleMode(mPowerManager != null
? (mPowerManager.isDeviceIdleMode()
|| mPowerManager.isLightDeviceIdleMode())
: false);
+ } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(action)) {
+ // Kick off pending jobs for any apps that re-appeared
+ mHandler.obtainMessage(MSG_CHECK_JOB).sendToTarget();
}
}
};
@@ -425,17 +429,24 @@
@Override
public void onBootPhase(int phase) {
if (PHASE_SYSTEM_SERVICES_READY == phase) {
- // Register br for package removals and user removals.
+ // Register for package removals and user removals.
final IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_REMOVED);
filter.addDataScheme("package");
getContext().registerReceiverAsUser(
mBroadcastReceiver, UserHandle.ALL, filter, null, null);
+
final IntentFilter userFilter = new IntentFilter(Intent.ACTION_USER_REMOVED);
userFilter.addAction(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED);
userFilter.addAction(PowerManager.ACTION_LIGHT_DEVICE_IDLE_MODE_CHANGED);
getContext().registerReceiverAsUser(
mBroadcastReceiver, UserHandle.ALL, userFilter, null, null);
- mPowerManager = (PowerManager)getContext().getSystemService(Context.POWER_SERVICE);
+
+ final IntentFilter storageFilter = new IntentFilter();
+ storageFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
+ getContext().registerReceiverAsUser(
+ mBroadcastReceiver, UserHandle.ALL, storageFilter, null, null);
+
+ mPowerManager = getContext().getSystemService(PowerManager.class);
try {
ActivityManagerNative.getDefault().registerUidObserver(mUidObserver,
ActivityManager.UID_OBSERVER_IDLE);
@@ -834,7 +845,7 @@
final boolean componentPresent;
try {
componentPresent = (AppGlobals.getPackageManager().getServiceInfo(
- job.getServiceComponent(), PackageManager.MATCH_ENCRYPTION_DEFAULT,
+ job.getServiceComponent(), PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
userId) != null);
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
@@ -924,8 +935,8 @@
final IPackageManager pm = AppGlobals.getPackageManager();
final ComponentName service = job.getService();
try {
- ServiceInfo si = pm.getServiceInfo(service, PackageManager.MATCH_ENCRYPTION_DEFAULT,
- UserHandle.getUserId(uid));
+ ServiceInfo si = pm.getServiceInfo(service,
+ PackageManager.MATCH_DEBUG_TRIAGED_MISSING, UserHandle.getUserId(uid));
if (si == null) {
throw new IllegalArgumentException("No such service " + service);
}
diff --git a/services/core/java/com/android/server/notification/ConditionProviders.java b/services/core/java/com/android/server/notification/ConditionProviders.java
index 1987214..ce18818 100644
--- a/services/core/java/com/android/server/notification/ConditionProviders.java
+++ b/services/core/java/com/android/server/notification/ConditionProviders.java
@@ -121,6 +121,11 @@
}
@Override
+ protected boolean checkType(IInterface service) {
+ return service instanceof IConditionProvider;
+ }
+
+ @Override
public void onBootPhaseAppsCanStart() {
super.onBootPhaseAppsCanStart();
for (int i = 0; i < mSystemConditionProviders.size(); i++) {
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index b7662da..09e6647 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -141,6 +141,8 @@
abstract protected IInterface asInterface(IBinder binder);
+ abstract protected boolean checkType(IInterface service);
+
abstract protected void onServiceAdded(ManagedServiceInfo info);
protected void onServiceRemovedLocked(ManagedServiceInfo removed) { }
@@ -169,7 +171,8 @@
if (filter != null && !filter.matches(info.component)) continue;
pw.println(" " + info.component
+ " (user " + info.userid + "): " + info.service
- + (info.isSystem?" SYSTEM":""));
+ + (info.isSystem?" SYSTEM":"")
+ + (info.isGuest(this)?" GUEST":""));
}
}
@@ -266,6 +269,18 @@
}
}
+ /**
+ * Add a service to our callbacks. The lifecycle of this service is managed externally,
+ * but unlike a system service, it should not be considered privledged.
+ * */
+ public void registerGuestService(ManagedServiceInfo guest) {
+ checkNotNull(guest.service);
+ checkType(guest.service);
+ if (registerServiceImpl(guest) != null) {
+ onServiceAdded(guest);
+ }
+ }
+
public void setCategoryState(String category, boolean enabled) {
synchronized (mMutex) {
final Boolean previous = mCategoryEnabled.put(category, enabled);
@@ -484,7 +499,7 @@
synchronized (mMutex) {
// Unbind automatically bound services, retain system services.
for (ManagedServiceInfo service : mServices) {
- if (!service.isSystem) {
+ if (!service.isSystem && !service.isGuest(this)) {
toRemove.add(service);
}
}
@@ -709,11 +724,15 @@
private ManagedServiceInfo registerServiceImpl(final IInterface service,
final ComponentName component, final int userid) {
+ ManagedServiceInfo info = newServiceInfo(service, component, userid,
+ true /*isSystem*/, null /*connection*/, Build.VERSION_CODES.LOLLIPOP);
+ return registerServiceImpl(info);
+ }
+
+ private ManagedServiceInfo registerServiceImpl(ManagedServiceInfo info) {
synchronized (mMutex) {
try {
- ManagedServiceInfo info = newServiceInfo(service, component, userid,
- true /*isSystem*/, null, Build.VERSION_CODES.LOLLIPOP);
- service.asBinder().linkToDeath(info, 0);
+ info.service.asBinder().linkToDeath(info, 0);
mServices.add(info);
return info;
} catch (RemoteException e) {
@@ -728,7 +747,7 @@
*/
private void unregisterServiceImpl(IInterface service, int userid) {
ManagedServiceInfo info = removeServiceImpl(service, userid);
- if (info != null && info.connection != null) {
+ if (info != null && info.connection != null && !info.isGuest(this)) {
mContext.unbindService(info.connection);
}
}
@@ -780,6 +799,10 @@
this.targetSdkVersion = targetSdkVersion;
}
+ public boolean isGuest(ManagedServices host) {
+ return ManagedServices.this != host;
+ }
+
@Override
public String toString() {
return new StringBuilder("ManagedServiceInfo[")
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 49aa73e..82c38af 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -39,6 +39,7 @@
import static org.xmlpull.v1.XmlPullParser.END_TAG;
import static org.xmlpull.v1.XmlPullParser.START_TAG;
+import android.Manifest;
import android.app.ActivityManager;
import android.app.ActivityManagerNative;
import android.app.AppGlobals;
@@ -96,6 +97,7 @@
import android.service.notification.IConditionProvider;
import android.service.notification.INotificationListener;
import android.service.notification.IStatusBarNotificationHolder;
+import android.service.notification.NotificationAssistantService;
import android.service.notification.NotificationListenerService;
import android.service.notification.NotificationRankingUpdate;
import android.service.notification.StatusBarNotification;
@@ -154,6 +156,7 @@
import java.util.List;
import java.util.Map.Entry;
import java.util.Objects;
+import java.util.concurrent.TimeUnit;
/** {@hide} */
public class NotificationManagerService extends SystemService {
@@ -167,11 +170,13 @@
// message codes
static final int MESSAGE_TIMEOUT = 2;
static final int MESSAGE_SAVE_POLICY_FILE = 3;
- static final int MESSAGE_RECONSIDER_RANKING = 4;
- static final int MESSAGE_RANKING_CONFIG_CHANGE = 5;
- static final int MESSAGE_SEND_RANKING_UPDATE = 6;
- static final int MESSAGE_LISTENER_HINTS_CHANGED = 7;
- static final int MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED = 8;
+ static final int MESSAGE_SEND_RANKING_UPDATE = 4;
+ static final int MESSAGE_LISTENER_HINTS_CHANGED = 5;
+ static final int MESSAGE_LISTENER_NOTIFICATION_FILTER_CHANGED = 6;
+
+ // ranking thread messages
+ private static final int MESSAGE_RECONSIDER_RANKING = 1000;
+ private static final int MESSAGE_RANKING_SORT = 1001;
static final int LONG_DELAY = 3500; // 3.5 seconds
static final int SHORT_DELAY = 2000; // 2 seconds
@@ -281,11 +286,13 @@
private final UserProfiles mUserProfiles = new UserProfiles();
private NotificationListeners mListeners;
+ private NotificationAssistant mAssistant;
private ConditionProviders mConditionProviders;
private NotificationUsageStats mUsageStats;
private static final int MY_UID = Process.myUid();
private static final int MY_PID = Process.myPid();
+ private RankingHandler mRankingHandler;
private static class Archive {
final int mBufferSize;
@@ -738,6 +745,7 @@
}
}
mListeners.onPackagesChanged(queryReplace, pkgList);
+ mAssistant.onPackagesChanged(queryReplace, pkgList);
mConditionProviders.onPackagesChanged(queryReplace, pkgList);
mRankingHelper.onPackagesChanged(queryReplace, pkgList);
}
@@ -779,6 +787,7 @@
// Refresh managed services
mConditionProviders.onUserSwitched(user);
mListeners.onUserSwitched(user);
+ mAssistant.onUserSwitched(user);
mZenModeHelper.onUserSwitched(user);
} else if (action.equals(Intent.ACTION_USER_ADDED)) {
mUserProfiles.updateCache(context);
@@ -874,8 +883,9 @@
extractorNames = new String[0];
}
mUsageStats = new NotificationUsageStats(getContext());
+ mRankingHandler = new RankingHandlerWorker(mRankingThread.getLooper());
mRankingHelper = new RankingHelper(getContext(),
- new RankingWorkerHandler(mRankingThread.getLooper()),
+ mRankingHandler,
mUsageStats,
extractorNames);
mConditionProviders = new ConditionProviders(getContext(), mHandler, mUserProfiles);
@@ -909,6 +919,7 @@
importOldBlockDb();
mListeners = new NotificationListeners();
+ mAssistant = new NotificationAssistant();
mStatusBar = getLocalService(StatusBarManagerInternal.class);
mStatusBar.setNotificationDelegate(mNotificationDelegate);
@@ -1025,6 +1036,7 @@
// bind to listener services.
mSettingsObserver.observe();
mListeners.onBootPhaseAppsCanStart();
+ mAssistant.onBootPhaseAppsCanStart();
mConditionProviders.onBootPhaseAppsCanStart();
}
}
@@ -1896,6 +1908,22 @@
Binder.restoreCallingIdentity(identity);
}
}
+
+ @Override
+ public void setImportanceFromAssistant(INotificationListener token, String key,
+ int importance, CharSequence explanation) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ synchronized (mNotificationList) {
+ mAssistant.checkServiceTokenLocked(token);
+ NotificationRecord n = mNotificationsByKey.get(key);
+ n.setImportance(importance, explanation);
+ mRankingHandler.requestSort();
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
};
private String disableNotificationEffects(NotificationRecord record) {
@@ -2037,6 +2065,8 @@
pw.print(listener.component);
}
pw.println(')');
+ pw.println("\n Notification assistant:");
+ mAssistant.dump(pw, filter);
}
pw.println("\n Policy access:");
pw.print(" mPolicyAccess: "); pw.println(mPolicyAccess);
@@ -2686,7 +2716,7 @@
}
}
- private void handleRankingConfigChange() {
+ private void handleRankingSort() {
synchronized (mNotificationList) {
final int N = mNotificationList.size();
ArrayList<String> orderBefore = new ArrayList<String>(N);
@@ -2788,9 +2818,9 @@
}
- private final class RankingWorkerHandler extends Handler
+ private final class RankingHandlerWorker extends Handler implements RankingHandler
{
- public RankingWorkerHandler(Looper looper) {
+ public RankingHandlerWorker(Looper looper) {
super(looper);
}
@@ -2800,11 +2830,23 @@
case MESSAGE_RECONSIDER_RANKING:
handleRankingReconsideration(msg);
break;
- case MESSAGE_RANKING_CONFIG_CHANGE:
- handleRankingConfigChange();
+ case MESSAGE_RANKING_SORT:
+ handleRankingSort();
break;
}
}
+
+ public void requestSort() {
+ removeMessages(MESSAGE_RANKING_SORT);
+ sendEmptyMessage(MESSAGE_RANKING_SORT);
+ }
+
+ public void requestReconsideration(RankingReconsideration recon) {
+ Message m = Message.obtain(this,
+ NotificationManagerService.MESSAGE_RECONSIDER_RANKING, recon);
+ long delay = recon.getDelay(TimeUnit.MILLISECONDS);
+ sendMessageDelayed(m, delay);
+ }
}
// Notifications
@@ -3305,6 +3347,45 @@
return true;
}
+ public class NotificationAssistant extends ManagedServices {
+
+ public NotificationAssistant() {
+ super(getContext(), mHandler, mNotificationList, mUserProfiles);
+ }
+
+ @Override
+ protected Config getConfig() {
+ Config c = new Config();
+ c.caption = "notification assistant";
+ c.serviceInterface = NotificationAssistantService.SERVICE_INTERFACE;
+ c.secureSettingName = Settings.Secure.ENABLED_NOTIFICATION_ASSISTANT;
+ c.bindPermission = Manifest.permission.BIND_NOTIFICATION_ASSISTANT_SERVICE;
+ c.settingsAction = Settings.ACTION_MANAGE_DEFAULT_APPS_SETTINGS;
+ c.clientLabel = R.string.notification_assistant_binding_label;
+ return c;
+ }
+
+ @Override
+ protected IInterface asInterface(IBinder binder) {
+ return INotificationListener.Stub.asInterface(binder);
+ }
+
+ @Override
+ protected boolean checkType(IInterface service) {
+ return service instanceof INotificationListener;
+ }
+
+ @Override
+ protected void onServiceAdded(ManagedServiceInfo info) {
+ mListeners.registerGuestService(info);
+ }
+
+ @Override
+ protected void onServiceRemovedLocked(ManagedServiceInfo removed) {
+ mListeners.unregisterService(removed.service, removed.userid);
+ }
+ }
+
public class NotificationListeners extends ManagedServices {
private final ArraySet<ManagedServiceInfo> mLightTrimListeners = new ArraySet<>();
@@ -3332,6 +3413,11 @@
}
@Override
+ protected boolean checkType(IInterface service) {
+ return service instanceof INotificationListener;
+ }
+
+ @Override
public void onServiceAdded(ManagedServiceInfo info) {
final INotificationListener listener = (INotificationListener) info.service;
final NotificationRankingUpdate update;
@@ -3366,7 +3452,6 @@
public int getOnNotificationPostedTrim(ManagedServiceInfo info) {
return mLightTrimListeners.contains(info) ? TRIM_LIGHT : TRIM_FULL;
-
}
/**
diff --git a/core/java/android/service/notification/NotificationAdjustment.aidl b/services/core/java/com/android/server/notification/RankingHandler.java
similarity index 77%
rename from core/java/android/service/notification/NotificationAdjustment.aidl
rename to services/core/java/com/android/server/notification/RankingHandler.java
index 805fe2c..80bb4f0 100644
--- a/core/java/android/service/notification/NotificationAdjustment.aidl
+++ b/services/core/java/com/android/server/notification/RankingHandler.java
@@ -13,7 +13,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package com.android.server.notification;
-package android.service.notification;
-
-parcelable NotificationAdjustment;
\ No newline at end of file
+public interface RankingHandler {
+ public void requestSort();
+ public void requestReconsideration(RankingReconsideration recon);
+}
diff --git a/services/core/java/com/android/server/notification/RankingHelper.java b/services/core/java/com/android/server/notification/RankingHelper.java
index 3287f67..0662e7a 100644
--- a/services/core/java/com/android/server/notification/RankingHelper.java
+++ b/services/core/java/com/android/server/notification/RankingHelper.java
@@ -19,10 +19,7 @@
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
-import android.os.Handler;
-import android.os.Message;
import android.os.UserHandle;
-import android.service.notification.NotificationListenerService;
import android.service.notification.NotificationListenerService.Ranking;
import android.text.TextUtils;
import android.util.ArrayMap;
@@ -40,7 +37,6 @@
import java.util.Collections;
import java.util.List;
import java.util.Map;
-import java.util.concurrent.TimeUnit;
public class RankingHelper implements RankingConfig {
private static final String TAG = "RankingHelper";
@@ -73,10 +69,10 @@
private final ArrayMap<String, Record> mRestoredWithoutUids = new ArrayMap<>(); // pkg => Record
private final Context mContext;
- private final Handler mRankingHandler;
+ private final RankingHandler mRankingHandler;
- public RankingHelper(Context context, Handler rankingHandler, NotificationUsageStats usageStats,
- String[] extractorNames) {
+ public RankingHelper(Context context, RankingHandler rankingHandler,
+ NotificationUsageStats usageStats, String[] extractorNames) {
mContext = context;
mRankingHandler = rankingHandler;
@@ -119,10 +115,7 @@
try {
RankingReconsideration recon = extractor.process(r);
if (recon != null) {
- Message m = Message.obtain(mRankingHandler,
- NotificationManagerService.MESSAGE_RECONSIDER_RANKING, recon);
- long delay = recon.getDelay(TimeUnit.MILLISECONDS);
- mRankingHandler.sendMessageDelayed(m, delay);
+ mRankingHandler.requestReconsideration(recon);
}
} catch (Throwable t) {
Slog.w(TAG, "NotificationSignalExtractor failed.", t);
@@ -287,7 +280,7 @@
for (int i = 0; i < N; i++) {
mSignalExtractors[i].setConfig(this);
}
- mRankingHandler.sendEmptyMessage(NotificationManagerService.MESSAGE_RANKING_CONFIG_CHANGE);
+ mRankingHandler.requestSort();
}
public void sort(ArrayList<NotificationRecord> notificationList) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 7bb6d1d..a07476c 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -106,6 +106,7 @@
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.AppsQueryHelper;
+import android.content.pm.ComponentInfo;
import android.content.pm.EphemeralApplicationInfo;
import android.content.pm.EphemeralResolveInfo;
import android.content.pm.EphemeralResolveInfo.EphemeralResolveIntentInfo;
@@ -207,7 +208,6 @@
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
-import com.android.internal.annotations.GuardedBy;
import com.android.internal.app.IMediaContainerService;
import com.android.internal.app.ResolverActivity;
import com.android.internal.content.NativeLibraryHelper;
@@ -308,7 +308,7 @@
private static final boolean DEBUG_DEXOPT = false;
private static final boolean DEBUG_ABI_SELECTION = false;
private static final boolean DEBUG_EPHEMERAL = false;
- private static final boolean DEBUG_ENCRYPTION_AWARE = false;
+ private static final boolean DEBUG_TRIAGED_MISSING = false;
static final boolean CLEAR_RUNTIME_PERMISSIONS_ON_UPGRADE = false;
@@ -2848,6 +2848,7 @@
@Override
public PackageInfo getPackageInfo(String packageName, int flags, int userId) {
if (!sUserManager.exists(userId)) return null;
+ flags = updateFlagsForPackage(flags, userId, packageName);
enforceCrossUserPermission(Binder.getCallingUid(), userId, false, false, "get package info");
// reader
synchronized (mPackages) {
@@ -2898,6 +2899,7 @@
@Override
public int getPackageUidEtc(String packageName, int flags, int userId) {
if (!sUserManager.exists(userId)) return -1;
+ flags = updateFlagsForPackage(flags, userId, packageName);
enforceCrossUserPermission(Binder.getCallingUid(), userId, false, false, "get package uid");
// reader
@@ -2924,10 +2926,8 @@
@Override
public int[] getPackageGidsEtc(String packageName, int flags, int userId) {
- if (!sUserManager.exists(userId)) {
- return null;
- }
-
+ if (!sUserManager.exists(userId)) return null;
+ flags = updateFlagsForPackage(flags, userId, packageName);
enforceCrossUserPermission(Binder.getCallingUid(), userId, false, false,
"getPackageGids");
@@ -2949,8 +2949,7 @@
return null;
}
- static PermissionInfo generatePermissionInfo(
- BasePermission bp, int flags) {
+ static PermissionInfo generatePermissionInfo(BasePermission bp, int flags) {
if (bp.perm != null) {
return PackageParser.generatePermissionInfo(bp.perm, flags);
}
@@ -3068,6 +3067,7 @@
@Override
public ApplicationInfo getApplicationInfo(String packageName, int flags, int userId) {
if (!sUserManager.exists(userId)) return null;
+ flags = updateFlagsForApplication(flags, userId, packageName);
enforceCrossUserPermission(Binder.getCallingUid(), userId, false, false, "get application info");
// writer
synchronized (mPackages) {
@@ -3182,46 +3182,88 @@
}
/**
- * Augment the given flags depending on current user running state. This is
- * purposefully done before acquiring {@link #mPackages} lock.
+ * Update given flags based on encryption status of current user.
*/
- private int augmentFlagsForUser(int flags, int userId, Object cookie) {
- if (cookie instanceof Intent) {
- // If intent claims to be triaged, then we're fine with default
- // matching behavior below
- final Intent intent = (Intent) cookie;
- if ((intent.getFlags() & Intent.FLAG_DEBUG_ENCRYPTION_TRIAGED) != 0) {
- flags |= PackageManager.MATCH_ENCRYPTION_DEFAULT;
- }
- }
-
- if ((flags & (PackageManager.MATCH_ENCRYPTION_UNAWARE_ONLY
- | PackageManager.MATCH_ENCRYPTION_AWARE_ONLY)) != 0) {
- // Caller expressed an opinion about what components they want to
- // see, so fall through and give them what they want
+ private int updateFlagsForEncryption(int flags, int userId) {
+ if ((flags & (PackageManager.MATCH_ENCRYPTION_UNAWARE
+ | PackageManager.MATCH_ENCRYPTION_AWARE)) != 0) {
+ // Caller expressed an explicit opinion about what encryption
+ // aware/unaware components they want to see, so fall through and
+ // give them what they want
} else {
// Caller expressed no opinion, so match based on user state
if (isUserKeyUnlocked(userId)) {
flags |= PackageManager.MATCH_ENCRYPTION_AWARE_AND_UNAWARE;
} else {
- flags |= PackageManager.MATCH_ENCRYPTION_AWARE_ONLY;
-
- // If we have a system caller that hasn't done their homework to
- // decide they want this default behavior, yell at them
- if (DEBUG_ENCRYPTION_AWARE && (Binder.getCallingUid() == Process.SYSTEM_UID)
- && ((flags & PackageManager.MATCH_ENCRYPTION_DEFAULT) == 0)) {
- Log.v(TAG, "Caller hasn't been triaged for FBE; they asked about " + cookie,
- new Throwable());
- }
+ flags |= PackageManager.MATCH_ENCRYPTION_AWARE;
}
}
return flags;
}
+ /**
+ * Update given flags when being used to request {@link PackageInfo}.
+ */
+ private int updateFlagsForPackage(int flags, int userId, Object cookie) {
+ boolean triaged = true;
+ if ((flags & PackageManager.GET_ACTIVITIES | PackageManager.GET_RECEIVERS
+ | PackageManager.GET_SERVICES | PackageManager.GET_PROVIDERS) != 0) {
+ // Caller is asking for component details, so they'd better be
+ // asking for specific encryption matching behavior, or be triaged
+ if ((flags & (PackageManager.MATCH_ENCRYPTION_UNAWARE
+ | PackageManager.MATCH_ENCRYPTION_AWARE
+ | PackageManager.MATCH_DEBUG_TRIAGED_MISSING)) == 0) {
+ triaged = false;
+ }
+ }
+ if ((flags & (PackageManager.MATCH_UNINSTALLED_PACKAGES
+ | PackageManager.MATCH_DEBUG_TRIAGED_MISSING)) == 0) {
+ triaged = false;
+ }
+ if (DEBUG_TRIAGED_MISSING && (Binder.getCallingUid() == Process.SYSTEM_UID) && !triaged) {
+ Log.w(TAG, "Caller hasn't been triaged for missing apps; they asked about " + cookie,
+ new Throwable());
+ }
+ return updateFlagsForEncryption(flags, userId);
+ }
+
+ /**
+ * Update given flags when being used to request {@link ApplicationInfo}.
+ */
+ private int updateFlagsForApplication(int flags, int userId, Object cookie) {
+ return updateFlagsForPackage(flags, userId, cookie);
+ }
+
+ /**
+ * Update given flags when being used to request {@link ComponentInfo}.
+ */
+ private int updateFlagsForComponent(int flags, int userId, Object cookie) {
+ boolean triaged = true;
+ // Caller is asking for component details, so they'd better be
+ // asking for specific encryption matching behavior, or be triaged
+ if ((flags & (PackageManager.MATCH_ENCRYPTION_UNAWARE
+ | PackageManager.MATCH_ENCRYPTION_AWARE
+ | PackageManager.MATCH_DEBUG_TRIAGED_MISSING)) == 0) {
+ triaged = false;
+ }
+ if (DEBUG_TRIAGED_MISSING && (Binder.getCallingUid() == Process.SYSTEM_UID) && !triaged) {
+ Log.w(TAG, "Caller hasn't been triaged for missing apps; they asked about " + cookie,
+ new Throwable());
+ }
+ return updateFlagsForEncryption(flags, userId);
+ }
+
+ /**
+ * Update given flags when being used to request {@link ResolveInfo}.
+ */
+ private int updateFlagsForResolve(int flags, int userId, Object cookie) {
+ return updateFlagsForComponent(flags, userId, cookie);
+ }
+
@Override
public ActivityInfo getActivityInfo(ComponentName component, int flags, int userId) {
if (!sUserManager.exists(userId)) return null;
- flags = augmentFlagsForUser(flags, userId, component);
+ flags = updateFlagsForComponent(flags, userId, component);
enforceCrossUserPermission(Binder.getCallingUid(), userId, false, false, "get activity info");
synchronized (mPackages) {
PackageParser.Activity a = mActivities.mActivities.get(component);
@@ -3266,7 +3308,7 @@
@Override
public ActivityInfo getReceiverInfo(ComponentName component, int flags, int userId) {
if (!sUserManager.exists(userId)) return null;
- flags = augmentFlagsForUser(flags, userId, component);
+ flags = updateFlagsForComponent(flags, userId, component);
enforceCrossUserPermission(Binder.getCallingUid(), userId, false, false, "get receiver info");
synchronized (mPackages) {
PackageParser.Activity a = mReceivers.mActivities.get(component);
@@ -3285,7 +3327,7 @@
@Override
public ServiceInfo getServiceInfo(ComponentName component, int flags, int userId) {
if (!sUserManager.exists(userId)) return null;
- flags = augmentFlagsForUser(flags, userId, component);
+ flags = updateFlagsForComponent(flags, userId, component);
enforceCrossUserPermission(Binder.getCallingUid(), userId, false, false, "get service info");
synchronized (mPackages) {
PackageParser.Service s = mServices.mServices.get(component);
@@ -3304,7 +3346,7 @@
@Override
public ProviderInfo getProviderInfo(ComponentName component, int flags, int userId) {
if (!sUserManager.exists(userId)) return null;
- flags = augmentFlagsForUser(flags, userId, component);
+ flags = updateFlagsForComponent(flags, userId, component);
enforceCrossUserPermission(Binder.getCallingUid(), userId, false, false, "get provider info");
synchronized (mPackages) {
PackageParser.Provider p = mProviders.mProviders.get(component);
@@ -4429,7 +4471,7 @@
public ResolveInfo resolveIntent(Intent intent, String resolvedType,
int flags, int userId) {
if (!sUserManager.exists(userId)) return null;
- flags = augmentFlagsForUser(flags, userId, intent);
+ flags = updateFlagsForResolve(flags, userId, intent);
enforceCrossUserPermission(Binder.getCallingUid(), userId, false, false, "resolve intent");
List<ResolveInfo> query = queryIntentActivities(intent, resolvedType, flags, userId);
final ResolveInfo bestChoice =
@@ -4687,7 +4729,7 @@
List<ResolveInfo> query, int priority, boolean always,
boolean removeMatches, boolean debug, int userId) {
if (!sUserManager.exists(userId)) return null;
- flags = augmentFlagsForUser(flags, userId, intent);
+ flags = updateFlagsForResolve(flags, userId, intent);
// writer
synchronized (mPackages) {
if (intent.getSelector() != null) {
@@ -4886,7 +4928,7 @@
public List<ResolveInfo> queryIntentActivities(Intent intent,
String resolvedType, int flags, int userId) {
if (!sUserManager.exists(userId)) return Collections.emptyList();
- flags = augmentFlagsForUser(flags, userId, intent);
+ flags = updateFlagsForResolve(flags, userId, intent);
enforceCrossUserPermission(Binder.getCallingUid(), userId, false, false, "query intent activities");
ComponentName comp = intent.getComponent();
if (comp == null) {
@@ -5370,7 +5412,7 @@
Intent[] specifics, String[] specificTypes, Intent intent,
String resolvedType, int flags, int userId) {
if (!sUserManager.exists(userId)) return Collections.emptyList();
- flags = augmentFlagsForUser(flags, userId, intent);
+ flags = updateFlagsForResolve(flags, userId, intent);
enforceCrossUserPermission(Binder.getCallingUid(), userId, false,
false, "query intent activity options");
final String resultsAction = intent.getAction();
@@ -5543,7 +5585,7 @@
public List<ResolveInfo> queryIntentReceivers(Intent intent, String resolvedType, int flags,
int userId) {
if (!sUserManager.exists(userId)) return Collections.emptyList();
- flags = augmentFlagsForUser(flags, userId, intent);
+ flags = updateFlagsForResolve(flags, userId, intent);
ComponentName comp = intent.getComponent();
if (comp == null) {
if (intent.getSelector() != null) {
@@ -5580,7 +5622,7 @@
@Override
public ResolveInfo resolveService(Intent intent, String resolvedType, int flags, int userId) {
if (!sUserManager.exists(userId)) return null;
- flags = augmentFlagsForUser(flags, userId, intent);
+ flags = updateFlagsForResolve(flags, userId, intent);
List<ResolveInfo> query = queryIntentServices(intent, resolvedType, flags, userId);
if (query != null) {
if (query.size() >= 1) {
@@ -5596,7 +5638,7 @@
public List<ResolveInfo> queryIntentServices(Intent intent, String resolvedType, int flags,
int userId) {
if (!sUserManager.exists(userId)) return Collections.emptyList();
- flags = augmentFlagsForUser(flags, userId, intent);
+ flags = updateFlagsForResolve(flags, userId, intent);
ComponentName comp = intent.getComponent();
if (comp == null) {
if (intent.getSelector() != null) {
@@ -5634,7 +5676,7 @@
public List<ResolveInfo> queryIntentContentProviders(
Intent intent, String resolvedType, int flags, int userId) {
if (!sUserManager.exists(userId)) return Collections.emptyList();
- flags = augmentFlagsForUser(flags, userId, intent);
+ flags = updateFlagsForResolve(flags, userId, intent);
ComponentName comp = intent.getComponent();
if (comp == null) {
if (intent.getSelector() != null) {
@@ -5670,8 +5712,9 @@
@Override
public ParceledListSlice<PackageInfo> getInstalledPackages(int flags, int userId) {
+ if (!sUserManager.exists(userId)) return ParceledListSlice.emptyList();
+ flags = updateFlagsForPackage(flags, userId, null);
final boolean listUninstalled = (flags & PackageManager.GET_UNINSTALLED_PACKAGES) != 0;
-
enforceCrossUserPermission(Binder.getCallingUid(), userId, true, false, "get installed packages");
// writer
@@ -5750,7 +5793,8 @@
@Override
public ParceledListSlice<PackageInfo> getPackagesHoldingPermissions(
String[] permissions, int flags, int userId) {
- if (!sUserManager.exists(userId)) return null;
+ if (!sUserManager.exists(userId)) return ParceledListSlice.emptyList();
+ flags = updateFlagsForPackage(flags, userId, permissions);
final boolean listUninstalled = (flags & PackageManager.GET_UNINSTALLED_PACKAGES) != 0;
// writer
@@ -5777,7 +5821,8 @@
@Override
public ParceledListSlice<ApplicationInfo> getInstalledApplications(int flags, int userId) {
- if (!sUserManager.exists(userId)) return null;
+ if (!sUserManager.exists(userId)) return ParceledListSlice.emptyList();
+ flags = updateFlagsForApplication(flags, userId, null);
final boolean listUninstalled = (flags & PackageManager.GET_UNINSTALLED_PACKAGES) != 0;
// writer
@@ -5920,7 +5965,7 @@
@Override
public ProviderInfo resolveContentProvider(String name, int flags, int userId) {
if (!sUserManager.exists(userId)) return null;
- flags = augmentFlagsForUser(flags, userId, name);
+ flags = updateFlagsForComponent(flags, userId, name);
// reader
synchronized (mPackages) {
final PackageParser.Provider provider = mProvidersByAuthority.get(name);
@@ -5972,7 +6017,7 @@
final int userId = processName != null ? UserHandle.getUserId(uid)
: UserHandle.getCallingUserId();
if (!sUserManager.exists(userId)) return null;
- flags = augmentFlagsForUser(flags, userId, processName);
+ flags = updateFlagsForComponent(flags, userId, processName);
ArrayList<ProviderInfo> finalList = null;
// reader
@@ -6009,8 +6054,7 @@
}
@Override
- public InstrumentationInfo getInstrumentationInfo(ComponentName name,
- int flags) {
+ public InstrumentationInfo getInstrumentationInfo(ComponentName name, int flags) {
// reader
synchronized (mPackages) {
final PackageParser.Instrumentation i = mInstrumentation.get(name);
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index d2a3ede..b330139 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -3846,9 +3846,9 @@
}
}
- final boolean matchesUnaware = ((flags & PackageManager.MATCH_ENCRYPTION_UNAWARE_ONLY) != 0)
+ final boolean matchesUnaware = ((flags & PackageManager.MATCH_ENCRYPTION_UNAWARE) != 0)
&& !componentInfo.encryptionAware;
- final boolean matchesAware = ((flags & PackageManager.MATCH_ENCRYPTION_AWARE_ONLY) != 0)
+ final boolean matchesAware = ((flags & PackageManager.MATCH_ENCRYPTION_AWARE) != 0)
&& componentInfo.encryptionAware;
return matchesUnaware || matchesAware;
}
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/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
index 29e3c63..549d2dc 100644
--- a/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
+++ b/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
@@ -126,7 +126,7 @@
final ComponentName keyguardComponent = ComponentName.unflattenFromString(
resources.getString(com.android.internal.R.string.config_keyguardComponent));
- intent.addFlags(Intent.FLAG_DEBUG_ENCRYPTION_TRIAGED);
+ intent.addFlags(Intent.FLAG_DEBUG_TRIAGED_MISSING);
intent.setComponent(keyguardComponent);
if (!context.bindServiceAsUser(intent, mKeyguardConnection,
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 0bbd23e..dd6493c 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -1322,7 +1322,7 @@
Intent intent = new Intent();
intent.setComponent(new ComponentName("com.android.systemui",
"com.android.systemui.SystemUIService"));
- intent.addFlags(Intent.FLAG_DEBUG_ENCRYPTION_TRIAGED);
+ intent.addFlags(Intent.FLAG_DEBUG_TRIAGED_MISSING);
//Slog.d(TAG, "Starting service: " + intent);
context.startServiceAsUser(intent, UserHandle.SYSTEM);
}
diff --git a/services/tests/servicestests/src/com/android/server/notification/RankingHelperTest.java b/services/tests/servicestests/src/com/android/server/notification/RankingHelperTest.java
index df7b412..2640889 100644
--- a/services/tests/servicestests/src/com/android/server/notification/RankingHelperTest.java
+++ b/services/tests/servicestests/src/com/android/server/notification/RankingHelperTest.java
@@ -35,7 +35,7 @@
public class RankingHelperTest extends AndroidTestCase {
@Mock NotificationUsageStats mUsageStats;
- @Mock Handler handler;
+ @Mock RankingHandler handler;
private Notification mNotiGroupGSortA;
private Notification mNotiGroupGSortB;
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/telecomm/java/android/telecom/DefaultDialerManager.java b/telecomm/java/android/telecom/DefaultDialerManager.java
index 27ee804..a915d37 100644
--- a/telecomm/java/android/telecom/DefaultDialerManager.java
+++ b/telecomm/java/android/telecom/DefaultDialerManager.java
@@ -155,8 +155,7 @@
// Get the list of apps registered for the DIAL intent with empty scheme
Intent intent = new Intent(Intent.ACTION_DIAL);
- List<ResolveInfo> resolveInfoList = packageManager.queryIntentActivities(intent,
- PackageManager.MATCH_ENCRYPTION_DEFAULT);
+ List<ResolveInfo> resolveInfoList = packageManager.queryIntentActivities(intent, 0);
List<String> packageNames = new ArrayList<>();
@@ -209,7 +208,7 @@
final List<String> result = new ArrayList<>();
final List<ResolveInfo> resolveInfoList = context.getPackageManager()
- .queryIntentActivities(intent, PackageManager.MATCH_ENCRYPTION_DEFAULT);
+ .queryIntentActivities(intent, 0);
final int length = resolveInfoList.size();
for (int i = 0; i < length; i++) {
final ActivityInfo info = resolveInfoList.get(i).activityInfo;
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 */