diff options
Diffstat (limited to 'core/java')
33 files changed, 1081 insertions, 419 deletions
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java index 8f125d7..2b35cd4 100644 --- a/core/java/android/app/ActivityManager.java +++ b/core/java/android/app/ActivityManager.java @@ -269,45 +269,51 @@ public class ActivityManager { * all activities that are visible to the user. */ public static final int PROCESS_STATE_TOP = 2; + /** @hide Process is hosting a foreground service. */ + public static final int PROCESS_STATE_FOREGROUND_SERVICE = 3; + + /** @hide Same as {@link #PROCESS_STATE_TOP} but while device is sleeping. */ + public static final int PROCESS_STATE_TOP_SLEEPING = 4; + /** @hide Process is important to the user, and something they are aware of. */ - public static final int PROCESS_STATE_IMPORTANT_FOREGROUND = 3; + public static final int PROCESS_STATE_IMPORTANT_FOREGROUND = 5; /** @hide Process is important to the user, but not something they are aware of. */ - public static final int PROCESS_STATE_IMPORTANT_BACKGROUND = 4; + public static final int PROCESS_STATE_IMPORTANT_BACKGROUND = 6; /** @hide Process is in the background running a backup/restore operation. */ - public static final int PROCESS_STATE_BACKUP = 5; + public static final int PROCESS_STATE_BACKUP = 7; /** @hide Process is in the background, but it can't restore its state so we want * to try to avoid killing it. */ - public static final int PROCESS_STATE_HEAVY_WEIGHT = 6; + public static final int PROCESS_STATE_HEAVY_WEIGHT = 8; /** @hide Process is in the background running a service. Unlike oom_adj, this level * is used for both the normal running in background state and the executing * operations state. */ - public static final int PROCESS_STATE_SERVICE = 7; + public static final int PROCESS_STATE_SERVICE = 9; /** @hide Process is in the background running a receiver. Note that from the * perspective of oom_adj receivers run at a higher foreground level, but for our * prioritization here that is not necessary and putting them below services means * many fewer changes in some process states as they receive broadcasts. */ - public static final int PROCESS_STATE_RECEIVER = 8; + public static final int PROCESS_STATE_RECEIVER = 10; /** @hide Process is in the background but hosts the home activity. */ - public static final int PROCESS_STATE_HOME = 9; + public static final int PROCESS_STATE_HOME = 11; /** @hide Process is in the background but hosts the last shown activity. */ - public static final int PROCESS_STATE_LAST_ACTIVITY = 10; + public static final int PROCESS_STATE_LAST_ACTIVITY = 12; /** @hide Process is being cached for later use and contains activities. */ - public static final int PROCESS_STATE_CACHED_ACTIVITY = 11; + public static final int PROCESS_STATE_CACHED_ACTIVITY = 13; /** @hide Process is being cached for later use and is a client of another cached * process that contains activities. */ - public static final int PROCESS_STATE_CACHED_ACTIVITY_CLIENT = 12; + public static final int PROCESS_STATE_CACHED_ACTIVITY_CLIENT = 14; /** @hide Process is being cached for later use and is empty. */ - public static final int PROCESS_STATE_CACHED_EMPTY = 13; + public static final int PROCESS_STATE_CACHED_EMPTY = 15; /** @hide requestType for assist context: only basic information. */ public static final int ASSIST_CONTEXT_BASIC = 0; @@ -2064,7 +2070,7 @@ public class ActivityManager { return ActivityManager.RunningAppProcessInfo.IMPORTANCE_CANT_SAVE_STATE; } else if (procState >= ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND) { return ActivityManager.RunningAppProcessInfo.IMPORTANCE_PERCEPTIBLE; - } else if (procState >= ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND) { + } else if (procState >= ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE) { return ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE; } else { return ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND; diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index ed05321..3b96fd5 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -5322,19 +5322,25 @@ public final class ActivityThread { private DropBoxManager dropBox; - public DropBoxReporter() { - dropBox = (DropBoxManager) getSystemContext().getSystemService(Context.DROPBOX_SERVICE); - } + public DropBoxReporter() {} @Override public void addData(String tag, byte[] data, int flags) { + ensureInitialized(); dropBox.addData(tag, data, flags); } @Override public void addText(String tag, String data) { + ensureInitialized(); dropBox.addText(tag, data); } + + private synchronized void ensureInitialized() { + if (dropBox == null) { + dropBox = (DropBoxManager) getSystemContext().getSystemService(Context.DROPBOX_SERVICE); + } + } } public static void main(String[] args) { diff --git a/core/java/android/app/AlarmManager.java b/core/java/android/app/AlarmManager.java index 5dd02ae..179957d 100644 --- a/core/java/android/app/AlarmManager.java +++ b/core/java/android/app/AlarmManager.java @@ -115,6 +115,40 @@ public class AlarmManager /** @hide */ public static final long WINDOW_HEURISTIC = -1; + /** + * Flag for alarms: this is to be a stand-alone alarm, that should not be batched with + * other alarms. + * @hide + */ + public static final int FLAG_STANDALONE = 1<<0; + + /** + * Flag for alarms: this alarm would like to wake the device even if it is idle. This + * is, for example, an alarm for an alarm clock. + * @hide + */ + public static final int FLAG_WAKE_FROM_IDLE = 1<<1; + + /** + * Flag for alarms: this alarm would like to still execute even if the device is + * idle. This won't bring the device out of idle, just allow this specific alarm to + * run. Note that this means the actual time this alarm goes off can be inconsistent + * with the time of non-allow-while-idle alarms (it could go earlier than the time + * requested by another alarm). + * + * @hide + */ + public static final int FLAG_ALLOW_WHILE_IDLE = 1<<2; + + /** + * Flag for alarms: this alarm marks the point where we would like to come out of idle + * mode. It may be moved by the alarm manager to match the first wake-from-idle alarm. + * Scheduling an alarm with this flag puts the alarm manager in to idle mode, where it + * avoids scheduling any further alarms until the marker alarm is executed. + * @hide + */ + public static final int FLAG_IDLE_UNTIL = 1<<3; + private final IAlarmManager mService; private final boolean mAlwaysExact; @@ -204,7 +238,7 @@ public class AlarmManager * @see #RTC_WAKEUP */ public void set(int type, long triggerAtMillis, PendingIntent operation) { - setImpl(type, triggerAtMillis, legacyExactLength(), 0, operation, null, null); + setImpl(type, triggerAtMillis, legacyExactLength(), 0, 0, operation, null, null); } /** @@ -265,7 +299,8 @@ public class AlarmManager */ public void setRepeating(int type, long triggerAtMillis, long intervalMillis, PendingIntent operation) { - setImpl(type, triggerAtMillis, legacyExactLength(), intervalMillis, operation, null, null); + setImpl(type, triggerAtMillis, legacyExactLength(), intervalMillis, 0, operation, null, + null); } /** @@ -315,7 +350,7 @@ public class AlarmManager */ public void setWindow(int type, long windowStartMillis, long windowLengthMillis, PendingIntent operation) { - setImpl(type, windowStartMillis, windowLengthMillis, 0, operation, null, null); + setImpl(type, windowStartMillis, windowLengthMillis, 0, 0, operation, null, null); } /** @@ -353,7 +388,16 @@ public class AlarmManager * @see #RTC_WAKEUP */ public void setExact(int type, long triggerAtMillis, PendingIntent operation) { - setImpl(type, triggerAtMillis, WINDOW_EXACT, 0, operation, null, null); + setImpl(type, triggerAtMillis, WINDOW_EXACT, 0, 0, operation, null, null); + } + + /** + * Schedule an idle-until alarm, which will keep the alarm manager idle until + * the given time. + * @hide + */ + public void setIdleUntil(int type, long triggerAtMillis, PendingIntent operation) { + setImpl(type, triggerAtMillis, WINDOW_EXACT, 0, FLAG_IDLE_UNTIL, operation, null, null); } /** @@ -381,18 +425,19 @@ public class AlarmManager * @see android.content.Intent#filterEquals */ public void setAlarmClock(AlarmClockInfo info, PendingIntent operation) { - setImpl(RTC_WAKEUP, info.getTriggerTime(), WINDOW_EXACT, 0, operation, null, info); + setImpl(RTC_WAKEUP, info.getTriggerTime(), WINDOW_EXACT, 0, 0, operation, null, info); } /** @hide */ @SystemApi public void set(int type, long triggerAtMillis, long windowMillis, long intervalMillis, PendingIntent operation, WorkSource workSource) { - setImpl(type, triggerAtMillis, windowMillis, intervalMillis, operation, workSource, null); + setImpl(type, triggerAtMillis, windowMillis, intervalMillis, 0, operation, workSource, + null); } private void setImpl(int type, long triggerAtMillis, long windowMillis, long intervalMillis, - PendingIntent operation, WorkSource workSource, AlarmClockInfo alarmClock) { + int flags, PendingIntent operation, WorkSource workSource, AlarmClockInfo alarmClock) { if (triggerAtMillis < 0) { /* NOTYET if (mAlwaysExact) { @@ -405,7 +450,7 @@ public class AlarmManager } try { - mService.set(type, triggerAtMillis, windowMillis, intervalMillis, operation, + mService.set(type, triggerAtMillis, windowMillis, intervalMillis, flags, operation, workSource, alarmClock); } catch (RemoteException ex) { } @@ -506,7 +551,7 @@ public class AlarmManager */ public void setInexactRepeating(int type, long triggerAtMillis, long intervalMillis, PendingIntent operation) { - setImpl(type, triggerAtMillis, WINDOW_HEURISTIC, intervalMillis, operation, null, null); + setImpl(type, triggerAtMillis, WINDOW_HEURISTIC, intervalMillis, 0, operation, null, null); } /** diff --git a/core/java/android/app/AlertDialog.java b/core/java/android/app/AlertDialog.java index 3e545f9..2dbbc38 100644 --- a/core/java/android/app/AlertDialog.java +++ b/core/java/android/app/AlertDialog.java @@ -134,6 +134,7 @@ public class AlertDialog extends Dialog implements DialogInterface { * {@code context}'s theme. * * @param context the parent context + * @see android.R.styleable#Theme_alertDialogTheme */ protected AlertDialog(Context context) { this(context, 0); @@ -155,6 +156,7 @@ public class AlertDialog extends Dialog implements DialogInterface { * {@code context}'s theme. * * @param context the parent context + * @see android.R.styleable#Theme_alertDialogTheme */ protected AlertDialog(Context context, boolean cancelable, OnCancelListener cancelListener) { this(context, 0); @@ -187,16 +189,24 @@ public class AlertDialog extends Dialog implements DialogInterface { * @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 + * @see android.R.styleable#Theme_alertDialogTheme */ protected AlertDialog(Context context, @AttrRes int themeResId) { - super(context, resolveDialogTheme(context, themeResId)); + this(context, themeResId, true); + } + + AlertDialog(Context context, @AttrRes int themeResId, boolean createContextThemeWrapper) { + super(context, createContextThemeWrapper ? resolveDialogTheme(context, themeResId) : 0, + createContextThemeWrapper); mWindow.alwaysReadCloseOnTouchAttr(); mAlert = new AlertController(getContext(), this, getWindow()); } static int resolveDialogTheme(Context context, int themeResId) { - if (themeResId == THEME_TRADITIONAL) { + if (themeResId == 0) { + return 0; + } else if (themeResId == THEME_TRADITIONAL) { return R.style.Theme_Dialog_Alert; } else if (themeResId == THEME_HOLO_DARK) { return R.style.Theme_Holo_Dialog_Alert; @@ -428,7 +438,6 @@ public class AlertDialog extends Dialog implements DialogInterface { public static class Builder { private final AlertController.AlertParams P; - private int mThemeResId; /** * Creates a builder for an alert dialog that uses the default alert @@ -473,7 +482,6 @@ public class AlertDialog extends Dialog implements DialogInterface { public Builder(Context context, int themeResId) { P = new AlertController.AlertParams(new ContextThemeWrapper( context, resolveDialogTheme(context, themeResId))); - mThemeResId = themeResId; } /** @@ -1075,7 +1083,7 @@ public class AlertDialog extends Dialog implements DialogInterface { * create and display the dialog. */ public AlertDialog create() { - final AlertDialog dialog = new AlertDialog(P.mContext, mThemeResId); + final AlertDialog dialog = new AlertDialog(P.mContext); P.apply(dialog.mAlert); dialog.setCancelable(P.mCancelable); if (P.mCancelable) { diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java index 9defcbe..6a2d207 100644 --- a/core/java/android/app/Dialog.java +++ b/core/java/android/app/Dialog.java @@ -21,7 +21,6 @@ import android.annotation.DrawableRes; import android.annotation.IdRes; import android.annotation.LayoutRes; import android.annotation.StringRes; -import com.android.internal.app.WindowDecorActionBar; import android.annotation.Nullable; import android.content.ComponentName; @@ -56,6 +55,9 @@ import android.view.Window; import android.view.WindowManager; import android.view.accessibility.AccessibilityEvent; +import com.android.internal.R; +import com.android.internal.app.WindowDecorActionBar; + import java.lang.ref.WeakReference; /** @@ -130,27 +132,32 @@ public class Dialog implements DialogInterface, Window.Callback, }; /** - * Create a Dialog window that uses the default dialog frame style. - * - * @param context The Context the Dialog is to run it. In particular, it - * uses the window manager and theme in this context to - * present its UI. + * Creates a dialog window that uses the default dialog theme. + * <p> + * The supplied {@code context} is used to obtain the window manager and + * base theme used to present the dialog. + * + * @param context the context in which the dialog should run + * @see android.R.styleable#Theme_dialogTheme */ public Dialog(Context context) { this(context, 0, true); } /** - * Create a Dialog window that uses a custom dialog style. + * Creates a dialog window that uses a custom dialog style. + * <p> + * The supplied {@code context} is used to obtain the window manager and + * base theme used to present the dialog. + * <p> + * The supplied {@code theme} is applied on top of the context's theme. See + * <a href="{@docRoot}guide/topics/resources/available-resources.html#stylesandthemes"> + * Style and Theme Resources</a> for more information about defining and + * using styles. * - * @param context The Context in which the Dialog should run. In particular, it - * uses the window manager and theme from this context to - * present its UI. - * @param theme A style resource describing the theme to use for the - * window. See <a href="{@docRoot}guide/topics/resources/available-resources.html#stylesandthemes">Style - * and Theme Resources</a> for more information about defining and using - * styles. This theme is applied on top of the current theme in - * <var>context</var>. If 0, the default dialog theme will be used. + * @param context the context in which the dialog should run + * @param theme a style resource describing the theme to use for the + * window, or {@code 0} to use the default dialog theme */ public Dialog(Context context, int theme) { this(context, theme, true); @@ -159,9 +166,8 @@ public class Dialog implements DialogInterface, Window.Callback, Dialog(Context context, int theme, boolean createContextThemeWrapper) { if (createContextThemeWrapper) { if (theme == 0) { - TypedValue outValue = new TypedValue(); - context.getTheme().resolveAttribute(com.android.internal.R.attr.dialogTheme, - outValue, true); + final TypedValue outValue = new TypedValue(); + context.getTheme().resolveAttribute(R.attr.dialogTheme, outValue, true); theme = outValue.resourceId; } mContext = new ContextThemeWrapper(context, theme); @@ -170,12 +176,14 @@ public class Dialog implements DialogInterface, Window.Callback, } mWindowManager = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE); - Window w = new PhoneWindow(mContext); + + final Window w = new PhoneWindow(mContext); mWindow = w; w.setCallback(this); w.setOnWindowDismissedCallback(this); w.setWindowManager(mWindowManager, null, null); w.setGravity(Gravity.CENTER); + mListenersHandler = new ListenersHandler(this); } diff --git a/core/java/android/app/IAlarmManager.aidl b/core/java/android/app/IAlarmManager.aidl index 194082e..d5719f5 100644 --- a/core/java/android/app/IAlarmManager.aidl +++ b/core/java/android/app/IAlarmManager.aidl @@ -28,7 +28,7 @@ import android.os.WorkSource; interface IAlarmManager { /** windowLength == 0 means exact; windowLength < 0 means the let the OS decide */ void set(int type, long triggerAtTime, long windowLength, - long interval, in PendingIntent operation, in WorkSource workSource, + long interval, int flags, in PendingIntent operation, in WorkSource workSource, in AlarmManager.AlarmClockInfo alarmClock); boolean setTime(long millis); void setTimeZone(String zone); diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl index 33262b3..e2230da 100644 --- a/core/java/android/app/INotificationManager.aidl +++ b/core/java/android/app/INotificationManager.aidl @@ -76,12 +76,10 @@ interface INotificationManager boolean matchesCallFilter(in Bundle extras); boolean isSystemConditionProviderEnabled(String path); + int getZenMode(); ZenModeConfig getZenModeConfig(); - boolean setZenModeConfig(in ZenModeConfig config); - oneway void setZenMode(int mode); + boolean setZenModeConfig(in ZenModeConfig config, String reason); + oneway void setZenMode(int mode, in Uri conditionId, String reason); oneway void notifyConditions(String pkg, in IConditionProvider provider, in Condition[] conditions); oneway void requestZenModeConditions(in IConditionListener callback, int relevance); - oneway void setZenModeCondition(in Condition condition); - oneway void setAutomaticZenModeConditions(in Uri[] conditionIds); - Condition[] getAutomaticZenModeConditions(); } diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java index 479327d..fa61e18 100644 --- a/core/java/android/app/NotificationManager.java +++ b/core/java/android/app/NotificationManager.java @@ -20,6 +20,7 @@ import android.annotation.SdkConstant; import android.app.Notification.Builder; import android.content.ComponentName; import android.content.Context; +import android.net.Uri; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; @@ -27,7 +28,7 @@ import android.os.RemoteException; import android.os.ServiceManager; import android.os.StrictMode; import android.os.UserHandle; -import android.service.notification.Condition; +import android.provider.Settings.Global; import android.service.notification.IConditionListener; import android.service.notification.ZenModeConfig; import android.util.Log; @@ -282,10 +283,10 @@ public class NotificationManager /** * @hide */ - public void setZenMode(int mode) { + public void setZenMode(int mode, Uri conditionId, String reason) { INotificationManager service = getService(); try { - service.setZenMode(mode); + service.setZenMode(mode, conditionId, reason); } catch (RemoteException e) { } } @@ -293,6 +294,18 @@ public class NotificationManager /** * @hide */ + public boolean setZenModeConfig(ZenModeConfig config, String reason) { + INotificationManager service = getService(); + try { + return service.setZenModeConfig(config, reason); + } catch (RemoteException e) { + return false; + } + } + + /** + * @hide + */ public void requestZenModeConditions(IConditionListener listener, int relevance) { INotificationManager service = getService(); try { @@ -304,24 +317,22 @@ public class NotificationManager /** * @hide */ - public void setZenModeCondition(Condition exitCondition) { + public int getZenMode() { INotificationManager service = getService(); try { - service.setZenModeCondition(exitCondition); + return service.getZenMode(); } catch (RemoteException e) { } + return Global.ZEN_MODE_OFF; } /** * @hide */ - public Condition getZenModeCondition() { + public ZenModeConfig getZenModeConfig() { INotificationManager service = getService(); try { - final ZenModeConfig config = service.getZenModeConfig(); - if (config != null) { - return config.exitCondition; - } + return service.getZenModeConfig(); } catch (RemoteException e) { } return null; diff --git a/core/java/android/app/VoiceInteractor.java b/core/java/android/app/VoiceInteractor.java index 7acf5f0..022a62c 100644 --- a/core/java/android/app/VoiceInteractor.java +++ b/core/java/android/app/VoiceInteractor.java @@ -318,7 +318,7 @@ public class VoiceInteractor { * @param label The label that will both be matched against what the user speaks * and displayed visually. * @param index The location of this option within the overall set of options. - * Can be used to help identify which the option when it is returned from the + * Can be used to help identify the option when it is returned from the * voice interactor. */ public Option(CharSequence label, int index) { diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 6b9d3f8..7523675 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -2778,7 +2778,7 @@ public class PackageParser { } /** - * Check if one of the IntentFilter as an action VIEW and a HTTP/HTTPS data URI + * Check if one of the IntentFilter as both actions DEFAULT / VIEW and a HTTP/HTTPS data URI */ private static boolean hasDomainURLs(Package pkg) { if (pkg == null || pkg.activities == null) return false; @@ -2792,6 +2792,7 @@ public class PackageParser { for (int m=0; m<countFilters; m++) { ActivityIntentInfo aii = filters.get(m); if (!aii.hasAction(Intent.ACTION_VIEW)) continue; + if (!aii.hasAction(Intent.ACTION_DEFAULT)) continue; if (aii.hasDataScheme(IntentFilter.SCHEME_HTTP) || aii.hasDataScheme(IntentFilter.SCHEME_HTTPS)) { Slog.d(TAG, "hasDomainURLs:true for package:" + pkg.packageName); diff --git a/core/java/android/database/AbstractCursor.java b/core/java/android/database/AbstractCursor.java index b5b89dd..581fe7f 100644 --- a/core/java/android/database/AbstractCursor.java +++ b/core/java/android/database/AbstractCursor.java @@ -35,34 +35,38 @@ public abstract class AbstractCursor implements CrossProcessCursor { private static final String TAG = "Cursor"; /** - * @deprecated This is never updated by this class and should not be used + * @removed This field should not be used. */ - @Deprecated protected HashMap<Long, Map<String, Object>> mUpdatedRows; - protected int mPos; - /** - * This must be set to the index of the row ID column by any - * subclass that wishes to support updates. - * - * @deprecated This field should not be used. + * @removed This field should not be used. */ - @Deprecated protected int mRowIdColumnIndex; /** - * If {@link #mRowIdColumnIndex} is not -1 this contains contains the value of - * the column at {@link #mRowIdColumnIndex} for the current row this cursor is - * pointing at. - * - * @deprecated This field should not be used. + * @removed This field should not be used. */ - @Deprecated protected Long mCurrentRowID; + /** + * @deprecated Use {@link #getPosition()} instead. + */ + @Deprecated + protected int mPos; + + /** + * @deprecated Use {@link #isClosed()} instead. + */ + @Deprecated protected boolean mClosed; + + /** + * @deprecated Do not use. + */ + @Deprecated protected ContentResolver mContentResolver; + private Uri mNotifyUri; private final Object mSelfObserverLock = new Object(); @@ -76,18 +80,28 @@ public abstract class AbstractCursor implements CrossProcessCursor { /* -------------------------------------------------------- */ /* These need to be implemented by subclasses */ + @Override abstract public int getCount(); + @Override abstract public String[] getColumnNames(); + @Override abstract public String getString(int column); + @Override abstract public short getShort(int column); + @Override abstract public int getInt(int column); + @Override abstract public long getLong(int column); + @Override abstract public float getFloat(int column); + @Override abstract public double getDouble(int column); + @Override abstract public boolean isNull(int column); + @Override public int getType(int column) { // Reflects the assumption that all commonly used field types (meaning everything // but blobs) are convertible to strings so it should be safe to call @@ -96,6 +110,7 @@ public abstract class AbstractCursor implements CrossProcessCursor { } // TODO implement getBlob in all cursor types + @Override public byte[] getBlob(int column) { throw new UnsupportedOperationException("getBlob is not supported"); } @@ -108,14 +123,17 @@ public abstract class AbstractCursor implements CrossProcessCursor { * * @return The pre-filled window that backs this cursor, or null if none. */ + @Override public CursorWindow getWindow() { return null; } + @Override public int getColumnCount() { return getColumnNames().length; } + @Override public void deactivate() { onDeactivateOrClose(); } @@ -129,6 +147,7 @@ public abstract class AbstractCursor implements CrossProcessCursor { mDataSetObservable.notifyInvalidated(); } + @Override public boolean requery() { if (mSelfObserver != null && mSelfObserverRegistered == false) { mContentResolver.registerContentObserver(mNotifyUri, true, mSelfObserver); @@ -138,10 +157,12 @@ public abstract class AbstractCursor implements CrossProcessCursor { return true; } + @Override public boolean isClosed() { return mClosed; } + @Override public void close() { mClosed = true; mContentObservable.unregisterAll(); @@ -158,11 +179,13 @@ public abstract class AbstractCursor implements CrossProcessCursor { * @param newPosition the position that we're moving to * @return true if the move is successful, false otherwise */ + @Override public boolean onMove(int oldPosition, int newPosition) { return true; } + @Override public void copyStringToBuffer(int columnIndex, CharArrayBuffer buffer) { // Default implementation, uses getString String result = getString(columnIndex); @@ -183,15 +206,14 @@ public abstract class AbstractCursor implements CrossProcessCursor { /* Implementation */ public AbstractCursor() { mPos = -1; - mRowIdColumnIndex = -1; - mCurrentRowID = null; - mUpdatedRows = new HashMap<Long, Map<String, Object>>(); } + @Override public final int getPosition() { return mPos; } + @Override public final boolean moveToPosition(int position) { // Make sure position isn't past the end of the cursor final int count = getCount(); @@ -216,9 +238,6 @@ public abstract class AbstractCursor implements CrossProcessCursor { mPos = -1; } else { mPos = position; - if (mRowIdColumnIndex != -1) { - mCurrentRowID = Long.valueOf(getLong(mRowIdColumnIndex)); - } } return result; @@ -229,35 +248,43 @@ public abstract class AbstractCursor implements CrossProcessCursor { DatabaseUtils.cursorFillWindow(this, position, window); } + @Override public final boolean move(int offset) { return moveToPosition(mPos + offset); } + @Override public final boolean moveToFirst() { return moveToPosition(0); } + @Override public final boolean moveToLast() { return moveToPosition(getCount() - 1); } + @Override public final boolean moveToNext() { return moveToPosition(mPos + 1); } + @Override public final boolean moveToPrevious() { return moveToPosition(mPos - 1); } + @Override public final boolean isFirst() { return mPos == 0 && getCount() != 0; } + @Override public final boolean isLast() { int cnt = getCount(); return mPos == (cnt - 1) && cnt != 0; } + @Override public final boolean isBeforeFirst() { if (getCount() == 0) { return true; @@ -265,6 +292,7 @@ public abstract class AbstractCursor implements CrossProcessCursor { return mPos == -1; } + @Override public final boolean isAfterLast() { if (getCount() == 0) { return true; @@ -272,6 +300,7 @@ public abstract class AbstractCursor implements CrossProcessCursor { return mPos == getCount(); } + @Override public int getColumnIndex(String columnName) { // Hack according to bug 903852 final int periodIndex = columnName.lastIndexOf('.'); @@ -297,6 +326,7 @@ public abstract class AbstractCursor implements CrossProcessCursor { return -1; } + @Override public int getColumnIndexOrThrow(String columnName) { final int index = getColumnIndex(columnName); if (index < 0) { @@ -305,14 +335,17 @@ public abstract class AbstractCursor implements CrossProcessCursor { return index; } + @Override public String getColumnName(int columnIndex) { return getColumnNames()[columnIndex]; } + @Override public void registerContentObserver(ContentObserver observer) { mContentObservable.registerObserver(observer); } + @Override public void unregisterContentObserver(ContentObserver observer) { // cursor will unregister all observers when it close if (!mClosed) { @@ -320,10 +353,12 @@ public abstract class AbstractCursor implements CrossProcessCursor { } } + @Override public void registerDataSetObserver(DataSetObserver observer) { mDataSetObservable.registerObserver(observer); } + @Override public void unregisterDataSetObserver(DataSetObserver observer) { mDataSetObservable.unregisterObserver(observer); } @@ -350,6 +385,7 @@ public abstract class AbstractCursor implements CrossProcessCursor { * @param notifyUri The URI to watch for changes. This can be a * specific row URI, or a base URI for a whole class of content. */ + @Override public void setNotificationUri(ContentResolver cr, Uri notifyUri) { setNotificationUri(cr, notifyUri, UserHandle.myUserId()); } @@ -368,31 +404,29 @@ public abstract class AbstractCursor implements CrossProcessCursor { } } + @Override public Uri getNotificationUri() { synchronized (mSelfObserverLock) { return mNotifyUri; } } + @Override public boolean getWantsAllOnMoveCalls() { return false; } - /** - * Sets a {@link Bundle} that will be returned by {@link #getExtras()}. <code>null</code> will - * be converted into {@link Bundle#EMPTY}. - * - * @param extras {@link Bundle} to set. - * @hide - */ + @Override public void setExtras(Bundle extras) { mExtras = (extras == null) ? Bundle.EMPTY : extras; } + @Override public Bundle getExtras() { return mExtras; } + @Override public Bundle respond(Bundle extras) { return Bundle.EMPTY; } diff --git a/core/java/android/database/BulkCursorToCursorAdaptor.java b/core/java/android/database/BulkCursorToCursorAdaptor.java index 98c7043..8576715 100644 --- a/core/java/android/database/BulkCursorToCursorAdaptor.java +++ b/core/java/android/database/BulkCursorToCursorAdaptor.java @@ -41,7 +41,6 @@ public final class BulkCursorToCursorAdaptor extends AbstractWindowedCursor { public void initialize(BulkCursorDescriptor d) { mBulkCursor = d.cursor; mColumns = d.columnNames; - mRowIdColumnIndex = DatabaseUtils.findRowIdColumnIndex(mColumns); mWantsAllOnMoveCalls = d.wantsAllOnMoveCalls; mCount = d.count; if (d.window != null) { diff --git a/core/java/android/database/Cursor.java b/core/java/android/database/Cursor.java index fc2a885..d10c9b8 100644 --- a/core/java/android/database/Cursor.java +++ b/core/java/android/database/Cursor.java @@ -444,6 +444,13 @@ public interface Cursor extends Closeable { boolean getWantsAllOnMoveCalls(); /** + * Sets a {@link Bundle} that will be returned by {@link #getExtras()}. + * + * @param extras {@link Bundle} to set, or null to set an empty bundle. + */ + void setExtras(Bundle extras); + + /** * Returns a bundle of extra values. This is an optional way for cursors to provide out-of-band * metadata to their users. One use of this is for reporting on the progress of network requests * that are required to fetch data for the cursor. diff --git a/core/java/android/database/CursorWrapper.java b/core/java/android/database/CursorWrapper.java index d8fcb17..63a2792 100644 --- a/core/java/android/database/CursorWrapper.java +++ b/core/java/android/database/CursorWrapper.java @@ -45,163 +45,210 @@ public class CursorWrapper implements Cursor { return mCursor; } + @Override public void close() { mCursor.close(); } + @Override public boolean isClosed() { return mCursor.isClosed(); } + @Override public int getCount() { return mCursor.getCount(); } + @Override + @Deprecated public void deactivate() { mCursor.deactivate(); } + @Override public boolean moveToFirst() { return mCursor.moveToFirst(); } + @Override public int getColumnCount() { return mCursor.getColumnCount(); } + @Override public int getColumnIndex(String columnName) { return mCursor.getColumnIndex(columnName); } + @Override public int getColumnIndexOrThrow(String columnName) throws IllegalArgumentException { return mCursor.getColumnIndexOrThrow(columnName); } + @Override public String getColumnName(int columnIndex) { return mCursor.getColumnName(columnIndex); } + @Override public String[] getColumnNames() { return mCursor.getColumnNames(); } + @Override public double getDouble(int columnIndex) { return mCursor.getDouble(columnIndex); } + @Override + public void setExtras(Bundle extras) { + mCursor.setExtras(extras); + } + + @Override public Bundle getExtras() { return mCursor.getExtras(); } + @Override public float getFloat(int columnIndex) { return mCursor.getFloat(columnIndex); } + @Override public int getInt(int columnIndex) { return mCursor.getInt(columnIndex); } + @Override public long getLong(int columnIndex) { return mCursor.getLong(columnIndex); } + @Override public short getShort(int columnIndex) { return mCursor.getShort(columnIndex); } + @Override public String getString(int columnIndex) { return mCursor.getString(columnIndex); } + @Override public void copyStringToBuffer(int columnIndex, CharArrayBuffer buffer) { mCursor.copyStringToBuffer(columnIndex, buffer); } + @Override public byte[] getBlob(int columnIndex) { return mCursor.getBlob(columnIndex); } + @Override public boolean getWantsAllOnMoveCalls() { return mCursor.getWantsAllOnMoveCalls(); } + @Override public boolean isAfterLast() { return mCursor.isAfterLast(); } + @Override public boolean isBeforeFirst() { return mCursor.isBeforeFirst(); } + @Override public boolean isFirst() { return mCursor.isFirst(); } + @Override public boolean isLast() { return mCursor.isLast(); } + @Override public int getType(int columnIndex) { return mCursor.getType(columnIndex); } + @Override public boolean isNull(int columnIndex) { return mCursor.isNull(columnIndex); } + @Override public boolean moveToLast() { return mCursor.moveToLast(); } + @Override public boolean move(int offset) { return mCursor.move(offset); } + @Override public boolean moveToPosition(int position) { return mCursor.moveToPosition(position); } + @Override public boolean moveToNext() { return mCursor.moveToNext(); } + @Override public int getPosition() { return mCursor.getPosition(); } + @Override public boolean moveToPrevious() { return mCursor.moveToPrevious(); } + @Override public void registerContentObserver(ContentObserver observer) { - mCursor.registerContentObserver(observer); + mCursor.registerContentObserver(observer); } + @Override public void registerDataSetObserver(DataSetObserver observer) { - mCursor.registerDataSetObserver(observer); + mCursor.registerDataSetObserver(observer); } + @Override + @Deprecated public boolean requery() { return mCursor.requery(); } + @Override public Bundle respond(Bundle extras) { return mCursor.respond(extras); } + @Override public void setNotificationUri(ContentResolver cr, Uri uri) { - mCursor.setNotificationUri(cr, uri); + mCursor.setNotificationUri(cr, uri); } + @Override public Uri getNotificationUri() { return mCursor.getNotificationUri(); } + @Override public void unregisterContentObserver(ContentObserver observer) { - mCursor.unregisterContentObserver(observer); + mCursor.unregisterContentObserver(observer); } + @Override public void unregisterDataSetObserver(DataSetObserver observer) { mCursor.unregisterDataSetObserver(observer); } diff --git a/core/java/android/database/sqlite/SQLiteCursor.java b/core/java/android/database/sqlite/SQLiteCursor.java index 5a1a8e2..2dc5ca4 100644 --- a/core/java/android/database/sqlite/SQLiteCursor.java +++ b/core/java/android/database/sqlite/SQLiteCursor.java @@ -105,7 +105,6 @@ public class SQLiteCursor extends AbstractWindowedCursor { mQuery = query; mColumns = query.getColumnNames(); - mRowIdColumnIndex = DatabaseUtils.findRowIdColumnIndex(mColumns); } /** diff --git a/core/java/android/os/BatteryManager.java b/core/java/android/os/BatteryManager.java index bd5a392..cccc4be 100644 --- a/core/java/android/os/BatteryManager.java +++ b/core/java/android/os/BatteryManager.java @@ -16,10 +16,12 @@ package android.os; +import android.content.Context; import android.os.BatteryProperty; import android.os.IBatteryPropertiesRegistrar; import android.os.RemoteException; import android.os.ServiceManager; +import com.android.internal.app.IBatteryStats; /** * The BatteryManager class contains strings and constants used for values @@ -128,6 +130,26 @@ public class BatteryManager { public static final int BATTERY_PLUGGED_ANY = BATTERY_PLUGGED_AC | BATTERY_PLUGGED_USB | BATTERY_PLUGGED_WIRELESS; + /** + * Sent when the device's battery has started charging (or has reached full charge + * and the device is on power). This is a good time to do work that you would like to + * avoid doing while on battery (that is to avoid draining the user's battery due to + * things they don't care enough about). + * + * This is paired with {@link #ACTION_DISCHARGING}. The current state can always + * be retrieved with {@link #isCharging()}. + */ + public static final String ACTION_CHARGING = "android.os.action.CHARGING"; + + /** + * Sent when the device's battery may be discharging, so apps should avoid doing + * extraneous work that would cause it to discharge faster. + * + * This is paired with {@link #ACTION_CHARGING}. The current state can always + * be retrieved with {@link #isCharging()}. + */ + public static final String ACTION_DISCHARGING = "android.os.action.DISCHARGING"; + /* * Battery property identifiers. These must match the values in * frameworks/native/include/batteryservice/BatteryService.h @@ -162,17 +184,34 @@ public class BatteryManager { */ public static final int BATTERY_PROPERTY_ENERGY_COUNTER = 5; + private final IBatteryStats mBatteryStats; private final IBatteryPropertiesRegistrar mBatteryPropertiesRegistrar; /** * @removed Was previously made visible by accident. */ public BatteryManager() { + mBatteryStats = IBatteryStats.Stub.asInterface( + ServiceManager.getService(BatteryStats.SERVICE_NAME)); mBatteryPropertiesRegistrar = IBatteryPropertiesRegistrar.Stub.asInterface( ServiceManager.getService("batteryproperties")); } /** + * Return true if the battery is currently considered to be charging. This means that + * the device is plugged in and is supplying sufficient power that the battery level is + * going up (or the battery is fully charged). Changes in this state are matched by + * broadcasts of {@link #ACTION_CHARGING} and {@link #ACTION_DISCHARGING}. + */ + public boolean isCharging() { + try { + return mBatteryStats.isCharging(); + } catch (RemoteException e) { + return true; + } + } + + /** * Query a battery property from the batteryproperties service. * * Returns the requested value, or Long.MIN_VALUE if property not diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java index 3051926..1566985 100644 --- a/core/java/android/os/BatteryStats.java +++ b/core/java/android/os/BatteryStats.java @@ -1065,6 +1065,7 @@ public abstract class BatteryStats implements Parcelable { public static final int STATE_SCREEN_ON_FLAG = 1<<20; public static final int STATE_BATTERY_PLUGGED_FLAG = 1<<19; public static final int STATE_PHONE_IN_CALL_FLAG = 1<<18; + public static final int STATE_CHARGING_FLAG = 1<<17; public static final int STATE_BLUETOOTH_ON_FLAG = 1<<16; public static final int MOST_INTERESTING_STATES = @@ -1751,6 +1752,7 @@ public abstract class BatteryStats implements Parcelable { new BitDescription(HistoryItem.STATE_SCREEN_ON_FLAG, "screen", "S"), new BitDescription(HistoryItem.STATE_BATTERY_PLUGGED_FLAG, "plugged", "BP"), new BitDescription(HistoryItem.STATE_PHONE_IN_CALL_FLAG, "phone_in_call", "Pcl"), + new BitDescription(HistoryItem.STATE_CHARGING_FLAG, "charging", "ch"), new BitDescription(HistoryItem.STATE_BLUETOOTH_ON_FLAG, "bluetooth", "b"), new BitDescription(HistoryItem.STATE_DATA_CONNECTION_MASK, HistoryItem.STATE_DATA_CONNECTION_SHIFT, "data_conn", "Pcn", diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 5ee8fb3..3087e1d 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -880,6 +880,15 @@ public final class Settings { "android.settings.VOICE_CONTROL_DO_NOT_DISTURB_MODE"; /** + * Activity Action: Show Zen Mode schedule rule configuration settings. + * + * @hide + */ + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_ZEN_MODE_SCHEDULE_RULE_SETTINGS + = "android.settings.ZEN_MODE_SCHEDULE_RULE_SETTINGS"; + + /** * Activity Action: Show the regulatory information screen for the device. * <p> * In some cases, a matching Activity may not exist, so ensure you safeguard @@ -7218,6 +7227,18 @@ public final class Settings { return "ZEN_MODE_OFF"; } + /** @hide */ public static boolean isValidZenMode(int value) { + switch (value) { + case Global.ZEN_MODE_OFF: + case Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS: + case Global.ZEN_MODE_ALARMS: + case Global.ZEN_MODE_NO_INTERRUPTIONS: + return true; + default: + return false; + } + } + /** * Opaque value, changes when persisted zen mode configuration changes. * diff --git a/core/java/android/security/keymaster/KeymasterDefs.java b/core/java/android/security/keymaster/KeymasterDefs.java index 5e2accd..dc5770b 100644 --- a/core/java/android/security/keymaster/KeymasterDefs.java +++ b/core/java/android/security/keymaster/KeymasterDefs.java @@ -59,9 +59,6 @@ public final class KeymasterDefs { public static final int KM_TAG_BLOB_USAGE_REQUIREMENTS = KM_ENUM | 705; public static final int KM_TAG_RSA_PUBLIC_EXPONENT = KM_LONG | 200; - public static final int KM_TAG_DSA_GENERATOR = KM_BIGNUM | 201; - public static final int KM_TAG_DSA_P = KM_BIGNUM | 202; - public static final int KM_TAG_DSA_Q = KM_BIGNUM | 203; public static final int KM_TAG_ACTIVE_DATETIME = KM_DATE | 400; public static final int KM_TAG_ORIGINATION_EXPIRE_DATETIME = KM_DATE | 401; public static final int KM_TAG_USAGE_EXPIRE_DATETIME = KM_DATE | 402; @@ -91,22 +88,8 @@ public final class KeymasterDefs { // Algorithm values. public static final int KM_ALGORITHM_RSA = 1; - public static final int KM_ALGORITHM_DSA = 2; - public static final int KM_ALGORITHM_ECDSA = 3; - public static final int KM_ALGORITHM_ECIES = 4; + public static final int KM_ALGORITHM_EC = 3; public static final int KM_ALGORITHM_AES = 32; - public static final int KM_ALGORITHM_3DES = 33; - public static final int KM_ALGORITHM_SKIPJACK = 34; - public static final int KM_ALGORITHM_MARS = 48; - public static final int KM_ALGORITHM_RC6 = 49; - public static final int KM_ALGORITHM_SERPENT = 50; - public static final int KM_ALGORITHM_TWOFISH = 51; - public static final int KM_ALGORITHM_IDEA = 52; - public static final int KM_ALGORITHM_RC5 = 53; - public static final int KM_ALGORITHM_CAST5 = 54; - public static final int KM_ALGORITHM_BLOWFISH = 55; - public static final int KM_ALGORITHM_RC4 = 64; - public static final int KM_ALGORITHM_CHACHA20 = 65; public static final int KM_ALGORITHM_HMAC = 128; // Block modes. @@ -219,7 +202,6 @@ public final class KeymasterDefs { public static final int KM_ERROR_INVALID_TAG = -40; public static final int KM_ERROR_MEMORY_ALLOCATION_FAILED = -41; public static final int KM_ERROR_INVALID_RESCOPING = -42; - public static final int KM_ERROR_INVALID_DSA_PARAMS = -43; public static final int KM_ERROR_IMPORT_PARAMETER_MISMATCH = -44; public static final int KM_ERROR_SECURE_HW_ACCESS_DENIED = -45; public static final int KM_ERROR_OPERATION_CANCELLED = -46; diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java index 2702457..56eb510 100644 --- a/core/java/android/service/notification/ZenModeConfig.java +++ b/core/java/android/service/notification/ZenModeConfig.java @@ -22,22 +22,25 @@ import android.content.res.Resources; import android.net.Uri; import android.os.Parcel; import android.os.Parcelable; +import android.provider.Settings.Global; import android.text.TextUtils; import android.text.format.DateFormat; +import android.util.ArrayMap; +import android.util.ArraySet; import android.util.Slog; +import com.android.internal.R; + import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlSerializer; import java.io.IOException; import java.util.ArrayList; -import java.util.Arrays; import java.util.Calendar; import java.util.Locale; import java.util.Objects; - -import com.android.internal.R; +import java.util.UUID; /** * Persisted configuration for zen mode. @@ -47,10 +50,6 @@ import com.android.internal.R; public class ZenModeConfig implements Parcelable { private static String TAG = "ZenModeConfig"; - public static final String SLEEP_MODE_NIGHTS = "nights"; - public static final String SLEEP_MODE_WEEKNIGHTS = "weeknights"; - public static final String SLEEP_MODE_DAYS_PREFIX = "days:"; - public static final int SOURCE_ANYONE = 0; public static final int SOURCE_CONTACT = 1; public static final int SOURCE_STAR = 2; @@ -60,6 +59,7 @@ public class ZenModeConfig implements Parcelable { Calendar.WEDNESDAY, Calendar.THURSDAY, Calendar.FRIDAY, Calendar.SATURDAY }; public static final int[] WEEKNIGHT_DAYS = { Calendar.SUNDAY, Calendar.MONDAY, Calendar.TUESDAY, Calendar.WEDNESDAY, Calendar.THURSDAY }; + public static final int[] WEEKEND_DAYS = { Calendar.FRIDAY, Calendar.SATURDAY }; public static final int[] MINUTE_BUCKETS = new int[] { 15, 30, 45, 60, 120, 180, 240, 480 }; private static final int SECONDS_MS = 1000; @@ -68,24 +68,18 @@ public class ZenModeConfig implements Parcelable { private static final boolean DEFAULT_ALLOW_REMINDERS = true; private static final boolean DEFAULT_ALLOW_EVENTS = true; + private static final boolean DEFAULT_ALLOW_REPEAT_CALLERS = false; - private static final int XML_VERSION = 1; + private static final int XML_VERSION = 2; private static final String ZEN_TAG = "zen"; private static final String ZEN_ATT_VERSION = "version"; private static final String ALLOW_TAG = "allow"; private static final String ALLOW_ATT_CALLS = "calls"; + private static final String ALLOW_ATT_REPEAT_CALLERS = "repeatCallers"; private static final String ALLOW_ATT_MESSAGES = "messages"; private static final String ALLOW_ATT_FROM = "from"; private static final String ALLOW_ATT_REMINDERS = "reminders"; private static final String ALLOW_ATT_EVENTS = "events"; - private static final String SLEEP_TAG = "sleep"; - private static final String SLEEP_ATT_MODE = "mode"; - private static final String SLEEP_ATT_NONE = "none"; - - private static final String SLEEP_ATT_START_HR = "startHour"; - private static final String SLEEP_ATT_START_MIN = "startMin"; - private static final String SLEEP_ATT_END_HR = "endHour"; - private static final String SLEEP_ATT_END_MIN = "endMin"; private static final String CONDITION_TAG = "condition"; private static final String CONDITION_ATT_COMPONENT = "component"; @@ -97,111 +91,115 @@ public class ZenModeConfig implements Parcelable { private static final String CONDITION_ATT_STATE = "state"; private static final String CONDITION_ATT_FLAGS = "flags"; - private static final String EXIT_CONDITION_TAG = "exitCondition"; - private static final String EXIT_CONDITION_ATT_COMPONENT = "component"; + private static final String MANUAL_TAG = "manual"; + private static final String AUTOMATIC_TAG = "automatic"; + + private static final String RULE_ATT_ID = "id"; + private static final String RULE_ATT_ENABLED = "enabled"; + private static final String RULE_ATT_SNOOZING = "snoozing"; + private static final String RULE_ATT_NAME = "name"; + private static final String RULE_ATT_COMPONENT = "component"; + private static final String RULE_ATT_ZEN = "zen"; + private static final String RULE_ATT_CONDITION_ID = "conditionId"; public boolean allowCalls; + public boolean allowRepeatCallers = DEFAULT_ALLOW_REPEAT_CALLERS; public boolean allowMessages; public boolean allowReminders = DEFAULT_ALLOW_REMINDERS; public boolean allowEvents = DEFAULT_ALLOW_EVENTS; public int allowFrom = SOURCE_ANYONE; - public String sleepMode; - public int sleepStartHour; // 0-23 - public int sleepStartMinute; // 0-59 - public int sleepEndHour; - public int sleepEndMinute; - public boolean sleepNone; // false = priority, true = none - public ComponentName[] conditionComponents; - public Uri[] conditionIds; - public Condition exitCondition; - public ComponentName exitConditionComponent; + public ZenRule manualRule; + public ArrayMap<String, ZenRule> automaticRules = new ArrayMap<>(); public ZenModeConfig() { } public ZenModeConfig(Parcel source) { allowCalls = source.readInt() == 1; + allowRepeatCallers = source.readInt() == 1; allowMessages = source.readInt() == 1; allowReminders = source.readInt() == 1; allowEvents = source.readInt() == 1; - if (source.readInt() == 1) { - sleepMode = source.readString(); - } - sleepStartHour = source.readInt(); - sleepStartMinute = source.readInt(); - sleepEndHour = source.readInt(); - sleepEndMinute = source.readInt(); - sleepNone = source.readInt() == 1; - int len = source.readInt(); - if (len > 0) { - conditionComponents = new ComponentName[len]; - source.readTypedArray(conditionComponents, ComponentName.CREATOR); - } - len = source.readInt(); + allowFrom = source.readInt(); + manualRule = source.readParcelable(null); + final int len = source.readInt(); if (len > 0) { - conditionIds = new Uri[len]; - source.readTypedArray(conditionIds, Uri.CREATOR); + final String[] ids = new String[len]; + final ZenRule[] rules = new ZenRule[len]; + source.readStringArray(ids); + source.readTypedArray(rules, ZenRule.CREATOR); + for (int i = 0; i < len; i++) { + automaticRules.put(ids[i], rules[i]); + } } - allowFrom = source.readInt(); - exitCondition = source.readParcelable(null); - exitConditionComponent = source.readParcelable(null); } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(allowCalls ? 1 : 0); + dest.writeInt(allowRepeatCallers ? 1 : 0); dest.writeInt(allowMessages ? 1 : 0); dest.writeInt(allowReminders ? 1 : 0); dest.writeInt(allowEvents ? 1 : 0); - if (sleepMode != null) { - dest.writeInt(1); - dest.writeString(sleepMode); - } else { - dest.writeInt(0); - } - dest.writeInt(sleepStartHour); - dest.writeInt(sleepStartMinute); - dest.writeInt(sleepEndHour); - dest.writeInt(sleepEndMinute); - dest.writeInt(sleepNone ? 1 : 0); - if (conditionComponents != null && conditionComponents.length > 0) { - dest.writeInt(conditionComponents.length); - dest.writeTypedArray(conditionComponents, 0); - } else { - dest.writeInt(0); - } - if (conditionIds != null && conditionIds.length > 0) { - dest.writeInt(conditionIds.length); - dest.writeTypedArray(conditionIds, 0); + dest.writeInt(allowFrom); + dest.writeParcelable(manualRule, 0); + if (!automaticRules.isEmpty()) { + final int len = automaticRules.size(); + final String[] ids = new String[len]; + final ZenRule[] rules = new ZenRule[len]; + for (int i = 0; i < len; i++) { + ids[i] = automaticRules.keyAt(i); + rules[i] = automaticRules.valueAt(i); + } + dest.writeInt(len); + dest.writeStringArray(ids); + dest.writeTypedArray(rules, 0); } else { dest.writeInt(0); } - dest.writeInt(allowFrom); - dest.writeParcelable(exitCondition, 0); - dest.writeParcelable(exitConditionComponent, 0); } @Override public String toString() { return new StringBuilder(ZenModeConfig.class.getSimpleName()).append('[') .append("allowCalls=").append(allowCalls) + .append(",allowRepeatCallers=").append(allowRepeatCallers) .append(",allowMessages=").append(allowMessages) .append(",allowFrom=").append(sourceToString(allowFrom)) .append(",allowReminders=").append(allowReminders) .append(",allowEvents=").append(allowEvents) - .append(",sleepMode=").append(sleepMode) - .append(",sleepStart=").append(sleepStartHour).append('.').append(sleepStartMinute) - .append(",sleepEnd=").append(sleepEndHour).append('.').append(sleepEndMinute) - .append(",sleepNone=").append(sleepNone) - .append(",conditionComponents=") - .append(conditionComponents == null ? null : TextUtils.join(",", conditionComponents)) - .append(",conditionIds=") - .append(conditionIds == null ? null : TextUtils.join(",", conditionIds)) - .append(",exitCondition=").append(exitCondition) - .append(",exitConditionComponent=").append(exitConditionComponent) + .append(",automaticRules=").append(automaticRules) + .append(",manualRule=").append(manualRule) .append(']').toString(); } + public boolean isValid() { + if (!isValidManualRule(manualRule)) return false; + final int N = automaticRules.size(); + for (int i = 0; i < N; i++) { + if (!isValidAutomaticRule(automaticRules.valueAt(i))) return false; + } + return true; + } + + private static boolean isValidManualRule(ZenRule rule) { + return rule == null || Global.isValidZenMode(rule.zenMode) && sameCondition(rule); + } + + private static boolean isValidAutomaticRule(ZenRule rule) { + return rule != null && !TextUtils.isEmpty(rule.name) && Global.isValidZenMode(rule.zenMode) + && rule.conditionId != null && sameCondition(rule); + } + + private static boolean sameCondition(ZenRule rule) { + if (rule == null) return false; + if (rule.conditionId == null) { + return rule.condition == null; + } else { + return rule.condition == null || rule.conditionId.equals(rule.condition.id); + } + } + public static String sourceToString(int source) { switch (source) { case SOURCE_ANYONE: @@ -221,49 +219,34 @@ public class ZenModeConfig implements Parcelable { if (o == this) return true; final ZenModeConfig other = (ZenModeConfig) o; return other.allowCalls == allowCalls + && other.allowRepeatCallers == allowRepeatCallers && other.allowMessages == allowMessages && other.allowFrom == allowFrom && other.allowReminders == allowReminders && other.allowEvents == allowEvents - && Objects.equals(other.sleepMode, sleepMode) - && other.sleepNone == sleepNone - && other.sleepStartHour == sleepStartHour - && other.sleepStartMinute == sleepStartMinute - && other.sleepEndHour == sleepEndHour - && other.sleepEndMinute == sleepEndMinute - && Objects.deepEquals(other.conditionComponents, conditionComponents) - && Objects.deepEquals(other.conditionIds, conditionIds) - && Objects.equals(other.exitCondition, exitCondition) - && Objects.equals(other.exitConditionComponent, exitConditionComponent); + && Objects.equals(other.automaticRules, automaticRules) + && Objects.equals(other.manualRule, manualRule); } @Override public int hashCode() { - return Objects.hash(allowCalls, allowMessages, allowFrom, allowReminders, allowEvents, - sleepMode, sleepNone, sleepStartHour, sleepStartMinute, sleepEndHour, - sleepEndMinute, Arrays.hashCode(conditionComponents), Arrays.hashCode(conditionIds), - exitCondition, exitConditionComponent); + return Objects.hash(allowCalls, allowRepeatCallers, allowMessages, allowFrom, + allowReminders, allowEvents, automaticRules, manualRule); } - public boolean isValid() { - return isValidHour(sleepStartHour) && isValidMinute(sleepStartMinute) - && isValidHour(sleepEndHour) && isValidMinute(sleepEndMinute) - && isValidSleepMode(sleepMode); - } - - public static boolean isValidSleepMode(String sleepMode) { - return sleepMode == null || sleepMode.equals(SLEEP_MODE_NIGHTS) - || sleepMode.equals(SLEEP_MODE_WEEKNIGHTS) || tryParseDays(sleepMode) != null; + private static String toDayList(int[] days) { + if (days == null || days.length == 0) return ""; + final StringBuilder sb = new StringBuilder(); + for (int i = 0; i < days.length; i++) { + if (i > 0) sb.append('.'); + sb.append(days[i]); + } + return sb.toString(); } - public static int[] tryParseDays(String sleepMode) { - if (sleepMode == null) return null; - sleepMode = sleepMode.trim(); - if (SLEEP_MODE_NIGHTS.equals(sleepMode)) return ALL_DAYS; - if (SLEEP_MODE_WEEKNIGHTS.equals(sleepMode)) return WEEKNIGHT_DAYS; - if (!sleepMode.startsWith(SLEEP_MODE_DAYS_PREFIX)) return null; - if (sleepMode.equals(SLEEP_MODE_DAYS_PREFIX)) return null; - final String[] tokens = sleepMode.substring(SLEEP_MODE_DAYS_PREFIX.length()).split(","); + private static int[] tryParseDayList(String dayList, String sep) { + if (dayList == null) return null; + final String[] tokens = dayList.split(sep); if (tokens.length == 0) return null; final int[] rt = new int[tokens.length]; for (int i = 0; i < tokens.length; i++) { @@ -283,7 +266,7 @@ public class ZenModeConfig implements Parcelable { } } - public static ZenModeConfig readXml(XmlPullParser parser) + public static ZenModeConfig readXml(XmlPullParser parser, Migration migration) throws XmlPullParserException, IOException { int type = parser.getEventType(); if (type != XmlPullParser.START_TAG) return null; @@ -291,21 +274,20 @@ public class ZenModeConfig implements Parcelable { if (!ZEN_TAG.equals(tag)) return null; final ZenModeConfig rt = new ZenModeConfig(); final int version = safeInt(parser, ZEN_ATT_VERSION, XML_VERSION); - final ArrayList<ComponentName> conditionComponents = new ArrayList<ComponentName>(); - final ArrayList<Uri> conditionIds = new ArrayList<Uri>(); + if (version == 1) { + final XmlV1 v1 = XmlV1.readXml(parser); + return migration.migrate(v1); + } while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) { tag = parser.getName(); if (type == XmlPullParser.END_TAG && ZEN_TAG.equals(tag)) { - if (!conditionComponents.isEmpty()) { - rt.conditionComponents = conditionComponents - .toArray(new ComponentName[conditionComponents.size()]); - rt.conditionIds = conditionIds.toArray(new Uri[conditionIds.size()]); - } return rt; } if (type == XmlPullParser.START_TAG) { if (ALLOW_TAG.equals(tag)) { rt.allowCalls = safeBoolean(parser, ALLOW_ATT_CALLS, false); + rt.allowRepeatCallers = safeBoolean(parser, ALLOW_ATT_REPEAT_CALLERS, + DEFAULT_ALLOW_REPEAT_CALLERS); rt.allowMessages = safeBoolean(parser, ALLOW_ATT_MESSAGES, false); rt.allowReminders = safeBoolean(parser, ALLOW_ATT_REMINDERS, DEFAULT_ALLOW_REMINDERS); @@ -314,31 +296,13 @@ public class ZenModeConfig implements Parcelable { if (rt.allowFrom < SOURCE_ANYONE || rt.allowFrom > MAX_SOURCE) { throw new IndexOutOfBoundsException("bad source in config:" + rt.allowFrom); } - } else if (SLEEP_TAG.equals(tag)) { - final String mode = parser.getAttributeValue(null, SLEEP_ATT_MODE); - rt.sleepMode = isValidSleepMode(mode)? mode : null; - rt.sleepNone = safeBoolean(parser, SLEEP_ATT_NONE, false); - final int startHour = safeInt(parser, SLEEP_ATT_START_HR, 0); - final int startMinute = safeInt(parser, SLEEP_ATT_START_MIN, 0); - final int endHour = safeInt(parser, SLEEP_ATT_END_HR, 0); - final int endMinute = safeInt(parser, SLEEP_ATT_END_MIN, 0); - rt.sleepStartHour = isValidHour(startHour) ? startHour : 0; - rt.sleepStartMinute = isValidMinute(startMinute) ? startMinute : 0; - rt.sleepEndHour = isValidHour(endHour) ? endHour : 0; - rt.sleepEndMinute = isValidMinute(endMinute) ? endMinute : 0; - } else if (CONDITION_TAG.equals(tag)) { - final ComponentName component = - safeComponentName(parser, CONDITION_ATT_COMPONENT); - final Uri conditionId = safeUri(parser, CONDITION_ATT_ID); - if (component != null && conditionId != null) { - conditionComponents.add(component); - conditionIds.add(conditionId); - } - } else if (EXIT_CONDITION_TAG.equals(tag)) { - rt.exitCondition = readConditionXml(parser); - if (rt.exitCondition != null) { - rt.exitConditionComponent = - safeComponentName(parser, EXIT_CONDITION_ATT_COMPONENT); + } else if (MANUAL_TAG.equals(tag)) { + rt.manualRule = readRuleXml(parser); + } else if (AUTOMATIC_TAG.equals(tag)) { + final String id = parser.getAttributeValue(null, RULE_ATT_ID); + final ZenRule automaticRule = readRuleXml(parser); + if (id != null && automaticRule != null) { + rt.automaticRules.put(id, automaticRule); } } } @@ -352,45 +316,68 @@ public class ZenModeConfig implements Parcelable { out.startTag(null, ALLOW_TAG); out.attribute(null, ALLOW_ATT_CALLS, Boolean.toString(allowCalls)); + out.attribute(null, ALLOW_ATT_REPEAT_CALLERS, Boolean.toString(allowRepeatCallers)); out.attribute(null, ALLOW_ATT_MESSAGES, Boolean.toString(allowMessages)); out.attribute(null, ALLOW_ATT_REMINDERS, Boolean.toString(allowReminders)); out.attribute(null, ALLOW_ATT_EVENTS, Boolean.toString(allowEvents)); out.attribute(null, ALLOW_ATT_FROM, Integer.toString(allowFrom)); out.endTag(null, ALLOW_TAG); - out.startTag(null, SLEEP_TAG); - if (sleepMode != null) { - out.attribute(null, SLEEP_ATT_MODE, sleepMode); - } - out.attribute(null, SLEEP_ATT_NONE, Boolean.toString(sleepNone)); - out.attribute(null, SLEEP_ATT_START_HR, Integer.toString(sleepStartHour)); - out.attribute(null, SLEEP_ATT_START_MIN, Integer.toString(sleepStartMinute)); - out.attribute(null, SLEEP_ATT_END_HR, Integer.toString(sleepEndHour)); - out.attribute(null, SLEEP_ATT_END_MIN, Integer.toString(sleepEndMinute)); - out.endTag(null, SLEEP_TAG); - - if (conditionComponents != null && conditionIds != null - && conditionComponents.length == conditionIds.length) { - for (int i = 0; i < conditionComponents.length; i++) { - out.startTag(null, CONDITION_TAG); - out.attribute(null, CONDITION_ATT_COMPONENT, - conditionComponents[i].flattenToString()); - out.attribute(null, CONDITION_ATT_ID, conditionIds[i].toString()); - out.endTag(null, CONDITION_TAG); - } + if (manualRule != null) { + out.startTag(null, MANUAL_TAG); + writeRuleXml(manualRule, out); + out.endTag(null, MANUAL_TAG); } - if (exitCondition != null && exitConditionComponent != null) { - out.startTag(null, EXIT_CONDITION_TAG); - out.attribute(null, EXIT_CONDITION_ATT_COMPONENT, - exitConditionComponent.flattenToString()); - writeConditionXml(exitCondition, out); - out.endTag(null, EXIT_CONDITION_TAG); + final int N = automaticRules.size(); + for (int i = 0; i < N; i++) { + final String id = automaticRules.keyAt(i); + final ZenRule automaticRule = automaticRules.valueAt(i); + out.startTag(null, AUTOMATIC_TAG); + out.attribute(null, RULE_ATT_ID, id); + writeRuleXml(automaticRule, out); + out.endTag(null, AUTOMATIC_TAG); } out.endTag(null, ZEN_TAG); } + public static ZenRule readRuleXml(XmlPullParser parser) { + final ZenRule rt = new ZenRule(); + rt.enabled = safeBoolean(parser, RULE_ATT_ENABLED, true); + rt.snoozing = safeBoolean(parser, RULE_ATT_SNOOZING, false); + rt.name = parser.getAttributeValue(null, RULE_ATT_NAME); + final String zen = parser.getAttributeValue(null, RULE_ATT_ZEN); + rt.zenMode = tryParseZenMode(zen, -1); + if (rt.zenMode == -1) { + Slog.w(TAG, "Bad zen mode in rule xml:" + zen); + return null; + } + rt.conditionId = safeUri(parser, RULE_ATT_CONDITION_ID); + rt.component = safeComponentName(parser, RULE_ATT_COMPONENT); + rt.condition = readConditionXml(parser); + return rt.condition != null ? rt : null; + } + + public static void writeRuleXml(ZenRule rule, XmlSerializer out) throws IOException { + out.attribute(null, RULE_ATT_ENABLED, Boolean.toString(rule.enabled)); + out.attribute(null, RULE_ATT_SNOOZING, Boolean.toString(rule.snoozing)); + if (rule.name != null) { + out.attribute(null, RULE_ATT_NAME, rule.name); + } + out.attribute(null, RULE_ATT_ZEN, Integer.toString(rule.zenMode)); + if (rule.component != null) { + out.attribute(null, RULE_ATT_COMPONENT, rule.component.flattenToString()); + } + if (rule.conditionId != null) { + out.attribute(null, RULE_ATT_CONDITION_ID, rule.conditionId.toString()); + } + if (rule.condition != null) { + writeConditionXml(rule.condition, out); + } + } + public static Condition readConditionXml(XmlPullParser parser) { final Uri id = safeUri(parser, CONDITION_ATT_ID); + if (id == null) return null; final String summary = parser.getAttributeValue(null, CONDITION_ATT_SUMMARY); final String line1 = parser.getAttributeValue(null, CONDITION_ATT_LINE1); final String line2 = parser.getAttributeValue(null, CONDITION_ATT_LINE2); @@ -446,6 +433,14 @@ public class ZenModeConfig implements Parcelable { return Uri.parse(val); } + public ArraySet<String> getAutomaticRuleNames() { + final ArraySet<String> rt = new ArraySet<String>(); + for (int i = 0; i < automaticRules.size(); i++) { + rt.add(automaticRules.valueAt(i).name); + } + return rt; + } + @Override public int describeContents() { return 0; @@ -475,17 +470,6 @@ public class ZenModeConfig implements Parcelable { } }; - public DowntimeInfo toDowntimeInfo() { - final DowntimeInfo downtime = new DowntimeInfo(); - downtime.startHour = sleepStartHour; - downtime.startMinute = sleepStartMinute; - downtime.endHour = sleepEndHour; - downtime.endMinute = sleepEndMinute; - downtime.mode = sleepMode; - downtime.none = sleepNone; - return downtime; - } - public static Condition toTimeCondition(Context context, int minutesFromNow, int userHandle) { final long now = System.currentTimeMillis(); final long millis = minutesFromNow == 0 ? ZERO_VALUE_MS : minutesFromNow * MINUTES_MS; @@ -548,38 +532,77 @@ public class ZenModeConfig implements Parcelable { return tryParseCountdownConditionId(conditionId) != 0; } - // Built-in downtime conditions - // e.g. condition://android/downtime?start=10.00&end=7.00&mode=days%3A5%2C6&none=false - public static final String DOWNTIME_PATH = "downtime"; + // built-in schedule conditions + public static final String SCHEDULE_PATH = "schedule"; + + public static class ScheduleInfo { + public int[] days; + public int startHour; + public int startMinute; + public int endHour; + public int endMinute; + + @Override + public int hashCode() { + return 0; + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof ScheduleInfo)) return false; + final ScheduleInfo other = (ScheduleInfo) o; + return toDayList(days).equals(toDayList(other.days)) + && startHour == other.startHour + && startMinute == other.startMinute + && endHour == other.endHour + && endMinute == other.endMinute; + } + + public ScheduleInfo copy() { + final ScheduleInfo rt = new ScheduleInfo(); + if (days != null) { + rt.days = new int[days.length]; + System.arraycopy(days, 0, rt.days, 0, days.length); + } + rt.startHour = startHour; + rt.startMinute = startMinute; + rt.endHour = endHour; + rt.endMinute = endMinute; + return rt; + } + } - public static Uri toDowntimeConditionId(DowntimeInfo downtime) { + public static Uri toScheduleConditionId(ScheduleInfo schedule) { return new Uri.Builder().scheme(Condition.SCHEME) .authority(SYSTEM_AUTHORITY) - .appendPath(DOWNTIME_PATH) - .appendQueryParameter("start", downtime.startHour + "." + downtime.startMinute) - .appendQueryParameter("end", downtime.endHour + "." + downtime.endMinute) - .appendQueryParameter("mode", downtime.mode) - .appendQueryParameter("none", Boolean.toString(downtime.none)) + .appendPath(SCHEDULE_PATH) + .appendQueryParameter("days", toDayList(schedule.days)) + .appendQueryParameter("start", schedule.startHour + "." + schedule.startMinute) + .appendQueryParameter("end", schedule.endHour + "." + schedule.endMinute) .build(); } - public static DowntimeInfo tryParseDowntimeConditionId(Uri conditionId) { - if (!Condition.isValidId(conditionId, SYSTEM_AUTHORITY) - || conditionId.getPathSegments().size() != 1 - || !DOWNTIME_PATH.equals(conditionId.getPathSegments().get(0))) { - return null; - } + public static boolean isValidScheduleConditionId(Uri conditionId) { + return tryParseScheduleConditionId(conditionId) != null; + } + + public static ScheduleInfo tryParseScheduleConditionId(Uri conditionId) { + final boolean isSchedule = conditionId != null + && conditionId.getScheme().equals(Condition.SCHEME) + && conditionId.getAuthority().equals(ZenModeConfig.SYSTEM_AUTHORITY) + && conditionId.getPathSegments().size() == 1 + && conditionId.getPathSegments().get(0).equals(ZenModeConfig.SCHEDULE_PATH); + if (!isSchedule) return null; final int[] start = tryParseHourAndMinute(conditionId.getQueryParameter("start")); final int[] end = tryParseHourAndMinute(conditionId.getQueryParameter("end")); if (start == null || end == null) return null; - final DowntimeInfo downtime = new DowntimeInfo(); - downtime.startHour = start[0]; - downtime.startMinute = start[1]; - downtime.endHour = end[0]; - downtime.endMinute = end[1]; - downtime.mode = conditionId.getQueryParameter("mode"); - downtime.none = Boolean.toString(true).equals(conditionId.getQueryParameter("none")); - return downtime; + final ScheduleInfo rt = new ScheduleInfo(); + rt.days = tryParseDayList(conditionId.getQueryParameter("days"), "\\."); + rt.startHour = start[0]; + rt.startMinute = start[1]; + rt.endHour = end[0]; + rt.endMinute = end[1]; + return rt; } private static int[] tryParseHourAndMinute(String value) { @@ -591,36 +614,268 @@ public class ZenModeConfig implements Parcelable { return isValidHour(hour) && isValidMinute(minute) ? new int[] { hour, minute } : null; } - public static boolean isValidDowntimeConditionId(Uri conditionId) { - return tryParseDowntimeConditionId(conditionId) != null; + private static int tryParseZenMode(String value, int defValue) { + final int rt = tryParseInt(value, defValue); + return Global.isValidZenMode(rt) ? rt : defValue; } - public static class DowntimeInfo { - public int startHour; // 0-23 - public int startMinute; // 0-59 - public int endHour; - public int endMinute; - public String mode; - public boolean none; + public String newRuleId() { + return UUID.randomUUID().toString().replace("-", ""); + } + + public static String getConditionLine1(Context context, ZenModeConfig config, + int userHandle) { + return getConditionLine(context, config, userHandle, true /*useLine1*/); + } + + public static String getConditionSummary(Context context, ZenModeConfig config, + int userHandle) { + return getConditionLine(context, config, userHandle, false /*useLine1*/); + } + + private static String getConditionLine(Context context, ZenModeConfig config, + int userHandle, boolean useLine1) { + if (config == null) return ""; + if (config.manualRule != null) { + final Uri id = config.manualRule.conditionId; + if (id == null) { + return context.getString(com.android.internal.R.string.zen_mode_forever); + } + final long time = tryParseCountdownConditionId(id); + Condition c = config.manualRule.condition; + if (time > 0) { + final long now = System.currentTimeMillis(); + final long span = time - now; + c = toTimeCondition(context, + time, Math.round(span / (float) MINUTES_MS), now, userHandle); + } + final String rt = c == null ? "" : useLine1 ? c.line1 : c.summary; + return TextUtils.isEmpty(rt) ? "" : rt; + } + String summary = ""; + for (ZenRule automaticRule : config.automaticRules.values()) { + if (automaticRule.enabled && !automaticRule.snoozing + && automaticRule.isTrueOrUnknown()) { + if (summary.isEmpty()) { + summary = automaticRule.name; + } else { + summary = context.getResources() + .getString(R.string.zen_mode_rule_name_combination, summary, + automaticRule.name); + } + } + } + return summary; + } + + public static class ZenRule implements Parcelable { + public boolean enabled; + public boolean snoozing; // user manually disabled this instance + public String name; // required for automatic (unique) + public int zenMode; + public Uri conditionId; // required for automatic + public Condition condition; // optional + public ComponentName component; // optional + + public ZenRule() { } + + public ZenRule(Parcel source) { + enabled = source.readInt() == 1; + snoozing = source.readInt() == 1; + if (source.readInt() == 1) { + name = source.readString(); + } + zenMode = source.readInt(); + conditionId = source.readParcelable(null); + condition = source.readParcelable(null); + component = source.readParcelable(null); + } @Override - public int hashCode() { + public int describeContents() { return 0; } @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(enabled ? 1 : 0); + dest.writeInt(snoozing ? 1 : 0); + if (name != null) { + dest.writeInt(1); + dest.writeString(name); + } else { + dest.writeInt(0); + } + dest.writeInt(zenMode); + dest.writeParcelable(conditionId, 0); + dest.writeParcelable(condition, 0); + dest.writeParcelable(component, 0); + } + + @Override + public String toString() { + return new StringBuilder(ZenRule.class.getSimpleName()).append('[') + .append("enabled=").append(enabled) + .append(",snoozing=").append(snoozing) + .append(",name=").append(name) + .append(",zenMode=").append(Global.zenModeToString(zenMode)) + .append(",conditionId=").append(conditionId) + .append(",condition=").append(condition) + .append(",component=").append(component) + .append(']').toString(); + } + + @Override public boolean equals(Object o) { - if (!(o instanceof DowntimeInfo)) return false; - final DowntimeInfo other = (DowntimeInfo) o; - return startHour == other.startHour - && startMinute == other.startMinute - && endHour == other.endHour - && endMinute == other.endMinute - && Objects.equals(mode, other.mode) - && none == other.none; + if (!(o instanceof ZenRule)) return false; + if (o == this) return true; + final ZenRule other = (ZenRule) o; + return other.enabled == enabled + && other.snoozing == snoozing + && Objects.equals(other.name, name) + && other.zenMode == zenMode + && Objects.equals(other.conditionId, conditionId) + && Objects.equals(other.condition, condition) + && Objects.equals(other.component, component); + } + + @Override + public int hashCode() { + return Objects.hash(enabled, snoozing, name, zenMode, conditionId, condition, + component); + } + + public boolean isTrueOrUnknown() { + return condition == null || condition.state == Condition.STATE_TRUE + || condition.state == Condition.STATE_UNKNOWN; + } + + public static final Parcelable.Creator<ZenRule> CREATOR + = new Parcelable.Creator<ZenRule>() { + @Override + public ZenRule createFromParcel(Parcel source) { + return new ZenRule(source); + } + @Override + public ZenRule[] newArray(int size) { + return new ZenRule[size]; + } + }; + } + + // Legacy config + public static final class XmlV1 { + public static final String SLEEP_MODE_NIGHTS = "nights"; + public static final String SLEEP_MODE_WEEKNIGHTS = "weeknights"; + public static final String SLEEP_MODE_DAYS_PREFIX = "days:"; + + private static final String EXIT_CONDITION_TAG = "exitCondition"; + private static final String EXIT_CONDITION_ATT_COMPONENT = "component"; + private static final String SLEEP_TAG = "sleep"; + private static final String SLEEP_ATT_MODE = "mode"; + private static final String SLEEP_ATT_NONE = "none"; + + private static final String SLEEP_ATT_START_HR = "startHour"; + private static final String SLEEP_ATT_START_MIN = "startMin"; + private static final String SLEEP_ATT_END_HR = "endHour"; + private static final String SLEEP_ATT_END_MIN = "endMin"; + + public boolean allowCalls; + public boolean allowMessages; + public boolean allowReminders = DEFAULT_ALLOW_REMINDERS; + public boolean allowEvents = DEFAULT_ALLOW_EVENTS; + public int allowFrom = SOURCE_ANYONE; + + public String sleepMode; // nights, weeknights, days:1,2,3 Calendar.days + public int sleepStartHour; // 0-23 + public int sleepStartMinute; // 0-59 + public int sleepEndHour; + public int sleepEndMinute; + public boolean sleepNone; // false = priority, true = none + public ComponentName[] conditionComponents; + public Uri[] conditionIds; + public Condition exitCondition; // manual exit condition + public ComponentName exitConditionComponent; // manual exit condition component + + private static boolean isValidSleepMode(String sleepMode) { + return sleepMode == null || sleepMode.equals(SLEEP_MODE_NIGHTS) + || sleepMode.equals(SLEEP_MODE_WEEKNIGHTS) || tryParseDays(sleepMode) != null; + } + + public static int[] tryParseDays(String sleepMode) { + if (sleepMode == null) return null; + sleepMode = sleepMode.trim(); + if (SLEEP_MODE_NIGHTS.equals(sleepMode)) return ALL_DAYS; + if (SLEEP_MODE_WEEKNIGHTS.equals(sleepMode)) return WEEKNIGHT_DAYS; + if (!sleepMode.startsWith(SLEEP_MODE_DAYS_PREFIX)) return null; + if (sleepMode.equals(SLEEP_MODE_DAYS_PREFIX)) return null; + return tryParseDayList(sleepMode.substring(SLEEP_MODE_DAYS_PREFIX.length()), ","); + } + + public static XmlV1 readXml(XmlPullParser parser) + throws XmlPullParserException, IOException { + int type; + String tag; + XmlV1 rt = new XmlV1(); + final ArrayList<ComponentName> conditionComponents = new ArrayList<ComponentName>(); + final ArrayList<Uri> conditionIds = new ArrayList<Uri>(); + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) { + tag = parser.getName(); + if (type == XmlPullParser.END_TAG && ZEN_TAG.equals(tag)) { + if (!conditionComponents.isEmpty()) { + rt.conditionComponents = conditionComponents + .toArray(new ComponentName[conditionComponents.size()]); + rt.conditionIds = conditionIds.toArray(new Uri[conditionIds.size()]); + } + return rt; + } + if (type == XmlPullParser.START_TAG) { + if (ALLOW_TAG.equals(tag)) { + rt.allowCalls = safeBoolean(parser, ALLOW_ATT_CALLS, false); + rt.allowMessages = safeBoolean(parser, ALLOW_ATT_MESSAGES, false); + rt.allowReminders = safeBoolean(parser, ALLOW_ATT_REMINDERS, + DEFAULT_ALLOW_REMINDERS); + rt.allowEvents = safeBoolean(parser, ALLOW_ATT_EVENTS, + DEFAULT_ALLOW_EVENTS); + rt.allowFrom = safeInt(parser, ALLOW_ATT_FROM, SOURCE_ANYONE); + if (rt.allowFrom < SOURCE_ANYONE || rt.allowFrom > MAX_SOURCE) { + throw new IndexOutOfBoundsException("bad source in config:" + + rt.allowFrom); + } + } else if (SLEEP_TAG.equals(tag)) { + final String mode = parser.getAttributeValue(null, SLEEP_ATT_MODE); + rt.sleepMode = isValidSleepMode(mode)? mode : null; + rt.sleepNone = safeBoolean(parser, SLEEP_ATT_NONE, false); + final int startHour = safeInt(parser, SLEEP_ATT_START_HR, 0); + final int startMinute = safeInt(parser, SLEEP_ATT_START_MIN, 0); + final int endHour = safeInt(parser, SLEEP_ATT_END_HR, 0); + final int endMinute = safeInt(parser, SLEEP_ATT_END_MIN, 0); + rt.sleepStartHour = isValidHour(startHour) ? startHour : 0; + rt.sleepStartMinute = isValidMinute(startMinute) ? startMinute : 0; + rt.sleepEndHour = isValidHour(endHour) ? endHour : 0; + rt.sleepEndMinute = isValidMinute(endMinute) ? endMinute : 0; + } else if (CONDITION_TAG.equals(tag)) { + final ComponentName component = + safeComponentName(parser, CONDITION_ATT_COMPONENT); + final Uri conditionId = safeUri(parser, CONDITION_ATT_ID); + if (component != null && conditionId != null) { + conditionComponents.add(component); + conditionIds.add(conditionId); + } + } else if (EXIT_CONDITION_TAG.equals(tag)) { + rt.exitCondition = readConditionXml(parser); + if (rt.exitCondition != null) { + rt.exitConditionComponent = + safeComponentName(parser, EXIT_CONDITION_ATT_COMPONENT); + } + } + } + } + throw new IllegalStateException("Failed to reach END_DOCUMENT"); } } - // built-in next alarm conditions - public static final String NEXT_ALARM_PATH = "next_alarm"; + public interface Migration { + ZenModeConfig migrate(XmlV1 v1); + } } diff --git a/core/java/android/text/method/AllCapsTransformationMethod.java b/core/java/android/text/method/AllCapsTransformationMethod.java index f9920dd..0cea821 100644 --- a/core/java/android/text/method/AllCapsTransformationMethod.java +++ b/core/java/android/text/method/AllCapsTransformationMethod.java @@ -19,6 +19,7 @@ import android.content.Context; import android.graphics.Rect; import android.util.Log; import android.view.View; +import android.widget.TextView; import java.util.Locale; @@ -39,11 +40,23 @@ public class AllCapsTransformationMethod implements TransformationMethod2 { @Override public CharSequence getTransformation(CharSequence source, View view) { - if (mEnabled) { - return source != null ? source.toString().toUpperCase(mLocale) : null; + if (!mEnabled) { + Log.w(TAG, "Caller did not enable length changes; not transforming text"); + return source; } - Log.w(TAG, "Caller did not enable length changes; not transforming text"); - return source; + + if (source == null) { + return null; + } + + Locale locale = null; + if (view instanceof TextView) { + locale = ((TextView)view).getTextLocale(); + } + if (locale == null) { + locale = mLocale; + } + return source.toString().toUpperCase(locale); } @Override diff --git a/core/java/android/text/method/BaseKeyListener.java b/core/java/android/text/method/BaseKeyListener.java index 63607fa..07c1ec3 100644 --- a/core/java/android/text/method/BaseKeyListener.java +++ b/core/java/android/text/method/BaseKeyListener.java @@ -22,6 +22,8 @@ import android.text.*; import android.text.method.TextKeyListener.Capitalize; import android.widget.TextView; +import java.text.BreakIterator; + /** * Abstract base class for key listeners. * @@ -63,9 +65,9 @@ public abstract class BaseKeyListener extends MetaKeyKeyListener private boolean backspaceOrForwardDelete(View view, Editable content, int keyCode, KeyEvent event, boolean isForwardDelete) { - // Ensure the key event does not have modifiers except ALT or SHIFT. + // Ensure the key event does not have modifiers except ALT or SHIFT or CTRL. if (!KeyEvent.metaStateHasNoModifiers(event.getMetaState() - & ~(KeyEvent.META_SHIFT_MASK | KeyEvent.META_ALT_MASK))) { + & ~(KeyEvent.META_SHIFT_MASK | KeyEvent.META_ALT_MASK | KeyEvent.META_CTRL_MASK))) { return false; } @@ -74,18 +76,28 @@ public abstract class BaseKeyListener extends MetaKeyKeyListener return true; } - // Alt+Backspace or Alt+ForwardDelete deletes the current line, if possible. - if (getMetaState(content, META_ALT_ON, event) == 1) { - if (deleteLine(view, content)) { - return true; + // MetaKeyKeyListener doesn't track control key state. Need to check the KeyEvent instead. + boolean isCtrlActive = ((event.getMetaState() & KeyEvent.META_CTRL_ON) != 0); + boolean isShiftActive = (getMetaState(content, META_SHIFT_ON, event) == 1); + boolean isAltActive = (getMetaState(content, META_ALT_ON, event) == 1); + + if (isCtrlActive) { + if (isAltActive || isShiftActive) { + // Ctrl+Alt, Ctrl+Shift, Ctrl+Alt+Shift should not delete any characters. + return false; } + return deleteUntilWordBoundary(view, content, isForwardDelete); + } + + // Alt+Backspace or Alt+ForwardDelete deletes the current line, if possible. + if (isAltActive && deleteLine(view, content)) { + return true; } // Delete a character. final int start = Selection.getSelectionEnd(content); final int end; - if (isForwardDelete || event.isShiftPressed() - || getMetaState(content, META_SHIFT_ON) == 1) { + if (isForwardDelete || event.isShiftPressed() || isShiftActive) { end = TextUtils.getOffsetAfter(content, start); } else { end = TextUtils.getOffsetBefore(content, start); @@ -97,6 +109,54 @@ public abstract class BaseKeyListener extends MetaKeyKeyListener return false; } + private boolean deleteUntilWordBoundary(View view, Editable content, boolean isForwardDelete) { + int currentCursorOffset = Selection.getSelectionStart(content); + + // If there is a selection, do nothing. + if (currentCursorOffset != Selection.getSelectionEnd(content)) { + return false; + } + + // Early exit if there is no contents to delete. + if ((!isForwardDelete && currentCursorOffset == 0) || + (isForwardDelete && currentCursorOffset == content.length())) { + return false; + } + + WordIterator wordIterator = null; + if (view instanceof TextView) { + wordIterator = ((TextView)view).getWordIterator(); + } + + if (wordIterator == null) { + // Default locale is used for WordIterator since the appropriate locale is not clear + // here. + // TODO: Use appropriate locale for WordIterator. + wordIterator = new WordIterator(); + } + + int deleteFrom; + int deleteTo; + + if (isForwardDelete) { + deleteFrom = currentCursorOffset; + wordIterator.setCharSequence(content, deleteFrom, content.length()); + deleteTo = wordIterator.following(currentCursorOffset); + if (deleteTo == BreakIterator.DONE) { + deleteTo = content.length(); + } + } else { + deleteTo = currentCursorOffset; + wordIterator.setCharSequence(content, 0, deleteTo); + deleteFrom = wordIterator.preceding(currentCursorOffset); + if (deleteFrom == BreakIterator.DONE) { + deleteFrom = 0; + } + } + content.delete(deleteFrom, deleteTo); + return true; + } + private boolean deleteSelection(View view, Editable content) { int selectionStart = Selection.getSelectionStart(content); int selectionEnd = Selection.getSelectionEnd(content); diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java index 32b99a8..5fb0c92 100644 --- a/core/java/android/widget/Editor.java +++ b/core/java/android/widget/Editor.java @@ -557,7 +557,7 @@ public class Editor { } } - private void hideInsertionPointCursorController() { + void hideInsertionPointCursorController() { if (mInsertionPointCursorController != null) { mInsertionPointCursorController.hide(); } @@ -1668,10 +1668,12 @@ public class Editor { if (mSelectionActionMode != null) { // Selection action mode is already started // TODO: revisit invocations to minimize this case. + mSelectionActionMode.invalidate(); return false; } ActionMode.Callback actionModeCallback = new SelectionActionModeCallback(); - mSelectionActionMode = mTextView.startActionMode(actionModeCallback); + mSelectionActionMode = mTextView.startActionMode( + actionModeCallback, ActionMode.TYPE_FLOATING); return mSelectionActionMode != null; } @@ -1681,6 +1683,7 @@ public class Editor { boolean startSelectionActionModeWithSelection() { if (mSelectionActionMode != null) { // Selection action mode is already started + mSelectionActionMode.invalidate(); return false; } @@ -1704,7 +1707,8 @@ public class Editor { // immediately hide the newly created action bar and would be visually distracting. if (!willExtract) { ActionMode.Callback actionModeCallback = new SelectionActionModeCallback(); - mSelectionActionMode = mTextView.startActionMode(actionModeCallback); + mSelectionActionMode = mTextView.startActionMode( + actionModeCallback, ActionMode.TYPE_FLOATING); } final boolean selectionStarted = mSelectionActionMode != null || willExtract; @@ -2963,6 +2967,28 @@ public class Editor { @Override public boolean onCreateActionMode(ActionMode mode, Menu menu) { + mode.setTitle(mTextView.getContext().getString( + com.android.internal.R.string.textSelectionCABTitle)); + mode.setSubtitle(null); + mode.setTitleOptionalHint(true); + populateMenuWithItems(menu); + + if (mCustomSelectionActionModeCallback != null) { + if (!mCustomSelectionActionModeCallback.onCreateActionMode(mode, menu)) { + // The custom mode can choose to cancel the action mode + return false; + } + } + + if (menu.hasVisibleItems() || mode.getCustomView() != null) { + mTextView.setHasTransientState(true); + return true; + } else { + return false; + } + } + + private void populateMenuWithItems(Menu menu) { final boolean legacy = mTextView.getContext().getApplicationInfo().targetSdkVersion < Build.VERSION_CODES.LOLLIPOP; final Context context = !legacy && menu instanceof MenuBuilder ? @@ -2971,11 +2997,6 @@ public class Editor { final TypedArray styledAttributes = context.obtainStyledAttributes( com.android.internal.R.styleable.SelectionModeDrawables); - mode.setTitle(mTextView.getContext().getString( - com.android.internal.R.string.textSelectionCABTitle)); - mode.setSubtitle(null); - mode.setTitleOptionalHint(true); - if (mTextView.canCut()) { menu.add(0, TextView.ID_CUT, 0, com.android.internal.R.string.cut). setIcon(styledAttributes.getResourceId( @@ -3010,37 +3031,33 @@ public class Editor { setShowAsAction( MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT); - if (mTextView.isSuggestionsEnabled() && isCursorInsideSuggestionSpan()) { - menu.add(0, TextView.ID_REPLACE, 0, com.android.internal.R.string.replace). - setShowAsAction( - MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT); - } + updateReplaceItem(menu); styledAttributes.recycle(); - - if (mCustomSelectionActionModeCallback != null) { - if (!mCustomSelectionActionModeCallback.onCreateActionMode(mode, menu)) { - // The custom mode can choose to cancel the action mode - return false; - } - } - - if (menu.hasVisibleItems() || mode.getCustomView() != null) { - mTextView.setHasTransientState(true); - return true; - } else { - return false; - } } @Override public boolean onPrepareActionMode(ActionMode mode, Menu menu) { + updateReplaceItem(menu); + if (mCustomSelectionActionModeCallback != null) { return mCustomSelectionActionModeCallback.onPrepareActionMode(mode, menu); } return true; } + private void updateReplaceItem(Menu menu) { + boolean canReplace = mTextView.isSuggestionsEnabled() && isCursorInsideSuggestionSpan(); + boolean replaceItemExists = menu.findItem(TextView.ID_REPLACE) != null; + if (canReplace && !replaceItemExists) { + menu.add(0, TextView.ID_REPLACE, 0, com.android.internal.R.string.replace). + setShowAsAction( + MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT); + } else if (!canReplace && replaceItemExists) { + menu.removeItem(TextView.ID_REPLACE); + } + } + @Override public boolean onActionItemClicked(ActionMode mode, MenuItem item) { if (mCustomSelectionActionModeCallback != null && @@ -3796,6 +3813,9 @@ public class Editor { Selection.setSelection((Spannable) mTextView.getText(), offset, mTextView.getSelectionEnd()); updateDrawable(); + if (mSelectionActionMode != null) { + mSelectionActionMode.invalidate(); + } } @Override @@ -3898,6 +3918,9 @@ public class Editor { public void updateSelection(int offset) { Selection.setSelection((Spannable) mTextView.getText(), mTextView.getSelectionStart(), offset); + if (mSelectionActionMode != null) { + mSelectionActionMode.invalidate(); + } updateDrawable(); } diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 9caa584..11439e4 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -9067,6 +9067,12 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } boolean selectAllText() { + // Need to hide insert point cursor controller before settings selection, otherwise insert + // point cursor controller obtains cursor update event and update cursor with cancelling + // selection. + if (mEditor != null) { + mEditor.hideInsertionPointCursorController(); + } final int length = mText.length(); Selection.setSelection((Spannable) mText, 0, length); return length > 0; diff --git a/core/java/com/android/internal/app/IBatteryStats.aidl b/core/java/com/android/internal/app/IBatteryStats.aidl index 1746bed..4f0e29e 100644 --- a/core/java/com/android/internal/app/IBatteryStats.aidl +++ b/core/java/com/android/internal/app/IBatteryStats.aidl @@ -40,6 +40,9 @@ interface IBatteryStats { ParcelFileDescriptor getStatisticsStream(); + // Return true if we see the battery as currently charging. + boolean isCharging(); + // Return the computed amount of time remaining on battery, in milliseconds. // Returns -1 if nothing could be computed. long computeBatteryTimeRemaining(); diff --git a/core/java/com/android/internal/app/ProcessStats.java b/core/java/com/android/internal/app/ProcessStats.java index 75beee9..fe79eff 100644 --- a/core/java/com/android/internal/app/ProcessStats.java +++ b/core/java/com/android/internal/app/ProcessStats.java @@ -140,6 +140,8 @@ public final class ProcessStats implements Parcelable { STATE_PERSISTENT, // ActivityManager.PROCESS_STATE_PERSISTENT STATE_PERSISTENT, // ActivityManager.PROCESS_STATE_PERSISTENT_UI STATE_TOP, // ActivityManager.PROCESS_STATE_TOP + STATE_IMPORTANT_FOREGROUND, // ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE + STATE_TOP, // ActivityManager.PROCESS_STATE_TOP_SLEEPING STATE_IMPORTANT_FOREGROUND, // ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND STATE_IMPORTANT_BACKGROUND, // ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND STATE_BACKUP, // ActivityManager.PROCESS_STATE_BACKUP diff --git a/core/java/com/android/internal/logging/EventLogTags.logtags b/core/java/com/android/internal/logging/EventLogTags.logtags index 870d20d..b9208ff 100644 --- a/core/java/com/android/internal/logging/EventLogTags.logtags +++ b/core/java/com/android/internal/logging/EventLogTags.logtags @@ -5,3 +5,5 @@ option java_package com.android.internal.logging; # interaction logs 524287 sysui_view_visibility (category|1|5),(visible|1|6) 524288 sysui_action (category|1|5) +524290 sysui_count (name|3),(increment|1) +524291 sysui_histogram (name|3),(bucket|1) diff --git a/core/java/com/android/internal/logging/MetricsLogger.java b/core/java/com/android/internal/logging/MetricsLogger.java index 0d5db77..6be6389 100644 --- a/core/java/com/android/internal/logging/MetricsLogger.java +++ b/core/java/com/android/internal/logging/MetricsLogger.java @@ -26,7 +26,9 @@ import android.os.Build; */ public class MetricsLogger implements MetricsConstants { // These constants are temporary, they should migrate to MetricsConstants. - // next value is 144; + // next value is 145; + + public static final int NOTIFICATION_ZEN_MODE_SCHEDULE_RULE = 144; public static void visible(Context context, int category) throws IllegalArgumentException { if (Build.IS_DEBUGGABLE && category == VIEW_UNKNOWN) { @@ -48,4 +50,14 @@ public class MetricsLogger implements MetricsConstants { } EventLogTags.writeSysuiAction(category); } + + /** Add an integer value to the monotonically increasing counter with the given name. */ + public static void count(Context context, String name, int value) { + EventLogTags.writeSysuiCount(name, value); + } + + /** Increment the bucket with the integer label on the histogram with the given name. */ + public static void histogram(Context context, String name, int bucket) { + EventLogTags.writeSysuiHistogram(name, bucket); + } } diff --git a/core/java/com/android/internal/midi/EventScheduler.java b/core/java/com/android/internal/midi/EventScheduler.java index 7b9a48c..506902f 100644 --- a/core/java/com/android/internal/midi/EventScheduler.java +++ b/core/java/com/android/internal/midi/EventScheduler.java @@ -16,6 +16,7 @@ package com.android.internal.midi; +import java.util.Iterator; import java.util.SortedMap; import java.util.TreeMap; @@ -28,7 +29,7 @@ public class EventScheduler { private static final long NANOS_PER_MILLI = 1000000; private final Object mLock = new Object(); - private SortedMap<Long, FastEventQueue> mEventBuffer; + volatile private SortedMap<Long, FastEventQueue> mEventBuffer; private FastEventQueue mEventPool = null; private int mMaxPoolSize = 200; private boolean mClosed; @@ -68,6 +69,7 @@ public class EventScheduler { mEventsRemoved++; SchedulableEvent event = mFirst; mFirst = event.mNext; + event.mNext = null; return event; } @@ -87,7 +89,7 @@ public class EventScheduler { */ public static class SchedulableEvent { private long mTimestamp; - private SchedulableEvent mNext = null; + volatile private SchedulableEvent mNext = null; /** * @param timestamp @@ -235,6 +237,11 @@ public class EventScheduler { return event; } + protected void flush() { + // Replace our event buffer with a fresh empty one + mEventBuffer = new TreeMap<Long, FastEventQueue>(); + } + public void close() { synchronized (mLock) { mClosed = true; diff --git a/core/java/com/android/internal/midi/MidiConstants.java b/core/java/com/android/internal/midi/MidiConstants.java index 87552e4..f78f75a 100644 --- a/core/java/com/android/internal/midi/MidiConstants.java +++ b/core/java/com/android/internal/midi/MidiConstants.java @@ -19,7 +19,7 @@ package com.android.internal.midi; /** * MIDI related constants and static methods. */ -public class MidiConstants { +public final class MidiConstants { public static final byte STATUS_COMMAND_MASK = (byte) 0xF0; public static final byte STATUS_CHANNEL_MASK = (byte) 0x0F; @@ -85,4 +85,16 @@ public class MidiConstants { } return (goodBytes == 0); } + + // Returns true if this command can be used for running status + public static boolean allowRunningStatus(int command) { + // only Channel Voice and Channel Mode commands can use running status + return (command >= STATUS_NOTE_OFF && command < STATUS_SYSTEM_EXCLUSIVE); + } + + // Returns true if this command cancels running status + public static boolean cancelsRunningStatus(int command) { + // System Common messages cancel running status + return (command >= STATUS_SYSTEM_EXCLUSIVE && command <= STATUS_END_SYSEX); + } } diff --git a/core/java/com/android/internal/midi/MidiDispatcher.java b/core/java/com/android/internal/midi/MidiDispatcher.java index 377bc68..70e699a 100644 --- a/core/java/com/android/internal/midi/MidiDispatcher.java +++ b/core/java/com/android/internal/midi/MidiDispatcher.java @@ -83,4 +83,11 @@ public final class MidiDispatcher extends MidiReceiver { } } } + + @Override + public void flush() throws IOException { + for (MidiReceiver receiver : mReceivers) { + receiver.flush(); + } + } } diff --git a/core/java/com/android/internal/midi/MidiEventScheduler.java b/core/java/com/android/internal/midi/MidiEventScheduler.java index 42d70f6..4dc5838 100644 --- a/core/java/com/android/internal/midi/MidiEventScheduler.java +++ b/core/java/com/android/internal/midi/MidiEventScheduler.java @@ -28,16 +28,9 @@ public class MidiEventScheduler extends EventScheduler { // Maintain a pool of scheduled events to reduce memory allocation. // This pool increases performance by about 14%. private final static int POOL_EVENT_SIZE = 16; - - private final MidiReceiver[] mReceivers; + private MidiReceiver mReceiver = new SchedulingReceiver(); private class SchedulingReceiver extends MidiReceiver { - private final int mPortNumber; - - public SchedulingReceiver(int portNumber) { - mPortNumber = portNumber; - } - /** * Store these bytes in the EventScheduler to be delivered at the specified * time. @@ -47,14 +40,17 @@ public class MidiEventScheduler extends EventScheduler { throws IOException { MidiEvent event = createScheduledEvent(msg, offset, count, timestamp); if (event != null) { - event.portNumber = mPortNumber; add(event); } } + + @Override + public void flush() { + MidiEventScheduler.this.flush(); + } } public static class MidiEvent extends SchedulableEvent { - public int portNumber; public int count = 0; public byte[] data; @@ -80,17 +76,6 @@ public class MidiEventScheduler extends EventScheduler { } } - public MidiEventScheduler() { - this(0); - } - - public MidiEventScheduler(int portCount) { - mReceivers = new MidiReceiver[portCount]; - for (int i = 0; i < portCount; i++) { - mReceivers[i] = new SchedulingReceiver(i); - } - } - /** * Create an event that contains the message. */ @@ -132,15 +117,7 @@ public class MidiEventScheduler extends EventScheduler { * @return the MidiReceiver */ public MidiReceiver getReceiver() { - return mReceivers[0]; - } - - /** - * This MidiReceiver will write date to the scheduling buffer. - * @return the MidiReceiver - */ - public MidiReceiver getReceiver(int portNumber) { - return mReceivers[portNumber]; + return mReceiver; } } diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index 793d0d3..c5c0ba6 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -22,6 +22,7 @@ import android.bluetooth.BluetoothActivityEnergyInfo; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothHeadset; import android.content.Context; +import android.content.Intent; import android.net.ConnectivityManager; import android.net.NetworkStats; import android.net.wifi.WifiActivityEnergyInfo; @@ -127,6 +128,7 @@ public final class BatteryStatsImpl extends BatteryStats { static final int MSG_UPDATE_WAKELOCKS = 1; static final int MSG_REPORT_POWER_CHANGE = 2; + static final int MSG_REPORT_CHARGING = 3; static final long DELAY_UPDATE_WAKELOCKS = 5*1000; private final KernelWakelockReader mKernelWakelockReader = new KernelWakelockReader(); @@ -135,6 +137,7 @@ public final class BatteryStatsImpl extends BatteryStats { public interface BatteryCallback { public void batteryNeedsCpuUpdate(); public void batteryPowerChanged(boolean onBattery); + public void batterySendBroadcast(Intent intent); } final class MyHandler extends Handler { @@ -156,6 +159,18 @@ public final class BatteryStatsImpl extends BatteryStats { cb.batteryPowerChanged(msg.arg1 != 0); } break; + case MSG_REPORT_CHARGING: + if (cb != null) { + final String action; + synchronized (BatteryStatsImpl.this) { + action = mCharging ? BatteryManager.ACTION_CHARGING + : BatteryManager.ACTION_DISCHARGING; + } + Intent intent = new Intent(action); + intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); + cb.batterySendBroadcast(intent); + } + break; } } } @@ -393,6 +408,12 @@ public final class BatteryStatsImpl extends BatteryStats { boolean mOnBattery; boolean mOnBatteryInternal; + /** + * External reporting of whether the device is actually charging. + */ + boolean mCharging = true; + int mLastChargingStateLevel; + /* * These keep track of battery levels (1-100) at the last plug event and the last unplug event. */ @@ -7243,6 +7264,10 @@ public final class BatteryStatsImpl extends BatteryStats { return mOnBattery; } + public boolean isCharging() { + return mCharging; + } + public boolean isScreenOn() { return mScreenState == Display.STATE_ON; } @@ -7802,6 +7827,20 @@ public final class BatteryStatsImpl extends BatteryStats { } } + boolean setChargingLocked(boolean charging) { + if (mCharging != charging) { + mCharging = charging; + if (charging) { + mHistoryCur.states |= HistoryItem.STATE_CHARGING_FLAG; + } else { + mHistoryCur.states &= ~HistoryItem.STATE_CHARGING_FLAG; + } + mHandler.sendEmptyMessage(MSG_REPORT_CHARGING); + return true; + } + return false; + } + void setOnBatteryLocked(final long mSecRealtime, final long mSecUptime, final boolean onBattery, final int oldStatus, final int level) { boolean doWrite = false; @@ -7861,6 +7900,10 @@ public final class BatteryStatsImpl extends BatteryStats { reset = true; mDischargeStepTracker.init(); } + if (mCharging) { + setChargingLocked(false); + } + mLastChargingStateLevel = level; mOnBattery = mOnBatteryInternal = true; mLastDischargeStepLevel = level; mMinDischargeStepLevel = level; @@ -7890,6 +7933,7 @@ public final class BatteryStatsImpl extends BatteryStats { mDischargeAmountScreenOff = 0; updateTimeBasesLocked(true, !screenOn, uptime, realtime); } else { + mLastChargingStateLevel = level; mOnBattery = mOnBatteryInternal = false; pullPendingStateUpdatesLocked(); mHistoryCur.batteryLevel = (byte)level; @@ -7982,10 +8026,13 @@ public final class BatteryStatsImpl extends BatteryStats { mHistoryCur.states |= HistoryItem.STATE_BATTERY_PLUGGED_FLAG; } } + // Always start out assuming charging, that will be updated later. + mHistoryCur.states |= HistoryItem.STATE_CHARGING_FLAG; mHistoryCur.batteryStatus = (byte)status; mHistoryCur.batteryLevel = (byte)level; mMaxChargeStepLevel = mMinDischargeStepLevel = mLastChargeStepLevel = mLastDischargeStepLevel = level; + mLastChargingStateLevel = level; } else if (mCurrentBatteryLevel != level || mOnBattery != onBattery) { recordDailyStatsIfNeededLocked(level >= 100 && onBattery); } @@ -8046,13 +8093,11 @@ public final class BatteryStatsImpl extends BatteryStats { mHistoryCur.batteryVoltage = (char)volt; changed = true; } - if (changed) { - addHistoryRecordLocked(elapsedRealtime, uptime); - } long modeBits = (((long)mInitStepMode) << STEP_LEVEL_INITIAL_MODE_SHIFT) | (((long)mModStepMode) << STEP_LEVEL_MODIFIED_MODE_SHIFT) | (((long)(level&0xff)) << STEP_LEVEL_LEVEL_SHIFT); if (onBattery) { + changed |= setChargingLocked(false); if (mLastDischargeStepLevel != level && mMinDischargeStepLevel > level) { mDischargeStepTracker.addLevelSteps(mLastDischargeStepLevel - level, modeBits, elapsedRealtime); @@ -8064,6 +8109,28 @@ public final class BatteryStatsImpl extends BatteryStats { mModStepMode = 0; } } else { + if (level >= 90) { + // If the battery level is at least 90%, always consider the device to be + // charging even if it happens to go down a level. + changed |= setChargingLocked(true); + mLastChargeStepLevel = level; + } if (!mCharging) { + if (mLastChargeStepLevel < level) { + // We have not reporting that we are charging, but the level has now + // gone up, so consider the state to be charging. + changed |= setChargingLocked(true); + mLastChargeStepLevel = level; + } + } else { + if (mLastChargeStepLevel > level) { + // We had reported that the device was charging, but here we are with + // power connected and the level going down. Looks like the current + // power supplied isn't enough, so consider the device to now be + // discharging. + changed |= setChargingLocked(false); + mLastChargeStepLevel = level; + } + } if (mLastChargeStepLevel != level && mMaxChargeStepLevel < level) { mChargeStepTracker.addLevelSteps(level - mLastChargeStepLevel, modeBits, elapsedRealtime); @@ -8075,6 +8142,9 @@ public final class BatteryStatsImpl extends BatteryStats { mModStepMode = 0; } } + if (changed) { + addHistoryRecordLocked(elapsedRealtime, uptime); + } } if (!onBattery && status == BatteryManager.BATTERY_STATUS_FULL) { // We don't record history while we are plugged in and fully charged. |