Merge "Mark the SUBSCRIBED_FEEDS_READ/WRITE permissions removed."
diff --git a/api/current.txt b/api/current.txt
index ea4aaf9..fdeda0b 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -3555,6 +3555,7 @@
method public int getMemoryClass();
method public void getMemoryInfo(android.app.ActivityManager.MemoryInfo);
method public static void getMyMemoryState(android.app.ActivityManager.RunningAppProcessInfo);
+ method public int getPackageImportance(java.lang.String);
method public android.os.Debug.MemoryInfo[] getProcessMemoryInfo(int[]);
method public java.util.List<android.app.ActivityManager.ProcessErrorStateInfo> getProcessesInErrorState();
method public deprecated java.util.List<android.app.ActivityManager.RecentTaskInfo> getRecentTasks(int, int) throws java.lang.SecurityException;
@@ -3647,9 +3648,11 @@
field public static final int IMPORTANCE_BACKGROUND = 400; // 0x190
field public static final int IMPORTANCE_EMPTY = 500; // 0x1f4
field public static final int IMPORTANCE_FOREGROUND = 100; // 0x64
+ field public static final int IMPORTANCE_FOREGROUND_SERVICE = 125; // 0x7d
field public static final int IMPORTANCE_GONE = 1000; // 0x3e8
field public static final int IMPORTANCE_PERCEPTIBLE = 130; // 0x82
field public static final int IMPORTANCE_SERVICE = 300; // 0x12c
+ field public static final int IMPORTANCE_TOP_SLEEPING = 150; // 0x96
field public static final int IMPORTANCE_VISIBLE = 200; // 0xc8
field public static final int REASON_PROVIDER_IN_USE = 1; // 0x1
field public static final int REASON_SERVICE_IN_USE = 2; // 0x2
@@ -13048,6 +13051,8 @@
method public abstract int captureBurst(java.util.List<android.hardware.camera2.CaptureRequest>, android.hardware.camera2.CameraCaptureSession.CaptureCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
method public abstract void close();
method public abstract android.hardware.camera2.CameraDevice getDevice();
+ method public abstract android.view.Surface getInputSurface();
+ method public abstract boolean isReprocessible();
method public abstract int setRepeatingBurst(java.util.List<android.hardware.camera2.CaptureRequest>, android.hardware.camera2.CameraCaptureSession.CaptureCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
method public abstract int setRepeatingRequest(android.hardware.camera2.CaptureRequest, android.hardware.camera2.CameraCaptureSession.CaptureCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
method public abstract void stopRepeating() throws android.hardware.camera2.CameraAccessException;
@@ -13163,6 +13168,8 @@
method public abstract void close();
method public abstract android.hardware.camera2.CaptureRequest.Builder createCaptureRequest(int) throws android.hardware.camera2.CameraAccessException;
method public abstract void createCaptureSession(java.util.List<android.view.Surface>, android.hardware.camera2.CameraCaptureSession.StateCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
+ method public abstract android.hardware.camera2.CaptureRequest.Builder createReprocessCaptureRequest(android.hardware.camera2.TotalCaptureResult) throws android.hardware.camera2.CameraAccessException;
+ method public abstract void createReprocessibleCaptureSession(android.hardware.camera2.params.InputConfiguration, java.util.List<android.view.Surface>, android.hardware.camera2.CameraCaptureSession.StateCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
method public abstract java.lang.String getId();
field public static final int TEMPLATE_MANUAL = 6; // 0x6
field public static final int TEMPLATE_PREVIEW = 1; // 0x1
@@ -13413,6 +13420,7 @@
method public int describeContents();
method public T get(android.hardware.camera2.CaptureRequest.Key<T>);
method public java.lang.Object getTag();
+ method public boolean isReprocess();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Boolean> BLACK_LEVEL_LOCK;
field public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Integer> COLOR_CORRECTION_ABERRATION_MODE;
@@ -13617,6 +13625,13 @@
field public static final int SCORE_MIN = 1; // 0x1
}
+ public final class InputConfiguration {
+ ctor public InputConfiguration(int, int, int);
+ method public int getFormat();
+ method public int getHeight();
+ method public int getWidth();
+ }
+
public final class LensShadingMap {
method public void copyGainFactors(float[], int);
method public int getColumnCount();
@@ -28665,7 +28680,7 @@
field public static final int START_WITH_SCREENSHOT = 2; // 0x2
}
- public abstract class VoiceInteractionSession implements android.view.KeyEvent.Callback {
+ public abstract class VoiceInteractionSession implements android.content.ComponentCallbacks2 android.view.KeyEvent.Callback {
ctor public VoiceInteractionSession(android.content.Context);
ctor public VoiceInteractionSession(android.content.Context, android.os.Handler);
method public void finish();
@@ -28681,6 +28696,7 @@
method public abstract void onCommand(android.service.voice.VoiceInteractionSession.Caller, android.service.voice.VoiceInteractionSession.Request, java.lang.String, android.os.Bundle);
method public void onCompleteVoice(android.service.voice.VoiceInteractionSession.Caller, android.service.voice.VoiceInteractionSession.Request, java.lang.CharSequence, android.os.Bundle);
method public void onComputeInsets(android.service.voice.VoiceInteractionSession.Insets);
+ method public void onConfigurationChanged(android.content.res.Configuration);
method public abstract void onConfirm(android.service.voice.VoiceInteractionSession.Caller, android.service.voice.VoiceInteractionSession.Request, java.lang.CharSequence, android.os.Bundle);
method public void onCreate(android.os.Bundle, int);
method public android.view.View onCreateContentView();
@@ -28693,10 +28709,12 @@
method public boolean onKeyLongPress(int, android.view.KeyEvent);
method public boolean onKeyMultiple(int, int, android.view.KeyEvent);
method public boolean onKeyUp(int, android.view.KeyEvent);
+ method public void onLowMemory();
method public abstract void onPickOption(android.service.voice.VoiceInteractionSession.Caller, android.service.voice.VoiceInteractionSession.Request, java.lang.CharSequence, android.app.VoiceInteractor.PickOptionRequest.Option[], android.os.Bundle);
method public void onShow(android.os.Bundle, int);
method public void onTaskFinished(android.content.Intent, int);
method public void onTaskStarted(android.content.Intent, int);
+ method public void onTrimMemory(int);
method public void setContentView(android.view.View);
method public void setKeepAwake(boolean);
method public void setTheme(int);
@@ -35569,6 +35587,8 @@
field public static final int TEXT_ALIGNMENT_VIEW_START = 5; // 0x5
field public static final int TEXT_DIRECTION_ANY_RTL = 2; // 0x2
field public static final int TEXT_DIRECTION_FIRST_STRONG = 1; // 0x1
+ field public static final int TEXT_DIRECTION_FIRST_STRONG_LTR = 6; // 0x6
+ field public static final int TEXT_DIRECTION_FIRST_STRONG_RTL = 7; // 0x7
field public static final int TEXT_DIRECTION_INHERIT = 0; // 0x0
field public static final int TEXT_DIRECTION_LOCALE = 5; // 0x5
field public static final int TEXT_DIRECTION_LTR = 3; // 0x3
diff --git a/api/system-current.txt b/api/system-current.txt
index 1a28546..deaf916 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -3644,6 +3644,7 @@
method public int getMemoryClass();
method public void getMemoryInfo(android.app.ActivityManager.MemoryInfo);
method public static void getMyMemoryState(android.app.ActivityManager.RunningAppProcessInfo);
+ method public int getPackageImportance(java.lang.String);
method public android.os.Debug.MemoryInfo[] getProcessMemoryInfo(int[]);
method public java.util.List<android.app.ActivityManager.ProcessErrorStateInfo> getProcessesInErrorState();
method public deprecated java.util.List<android.app.ActivityManager.RecentTaskInfo> getRecentTasks(int, int) throws java.lang.SecurityException;
@@ -3736,9 +3737,11 @@
field public static final int IMPORTANCE_BACKGROUND = 400; // 0x190
field public static final int IMPORTANCE_EMPTY = 500; // 0x1f4
field public static final int IMPORTANCE_FOREGROUND = 100; // 0x64
+ field public static final int IMPORTANCE_FOREGROUND_SERVICE = 125; // 0x7d
field public static final int IMPORTANCE_GONE = 1000; // 0x3e8
field public static final int IMPORTANCE_PERCEPTIBLE = 130; // 0x82
field public static final int IMPORTANCE_SERVICE = 300; // 0x12c
+ field public static final int IMPORTANCE_TOP_SLEEPING = 150; // 0x96
field public static final int IMPORTANCE_VISIBLE = 200; // 0xc8
field public static final int REASON_PROVIDER_IN_USE = 1; // 0x1
field public static final int REASON_SERVICE_IN_USE = 2; // 0x2
@@ -13344,6 +13347,8 @@
method public abstract int captureBurst(java.util.List<android.hardware.camera2.CaptureRequest>, android.hardware.camera2.CameraCaptureSession.CaptureCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
method public abstract void close();
method public abstract android.hardware.camera2.CameraDevice getDevice();
+ method public abstract android.view.Surface getInputSurface();
+ method public abstract boolean isReprocessible();
method public abstract int setRepeatingBurst(java.util.List<android.hardware.camera2.CaptureRequest>, android.hardware.camera2.CameraCaptureSession.CaptureCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
method public abstract int setRepeatingRequest(android.hardware.camera2.CaptureRequest, android.hardware.camera2.CameraCaptureSession.CaptureCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
method public abstract void stopRepeating() throws android.hardware.camera2.CameraAccessException;
@@ -13459,6 +13464,8 @@
method public abstract void close();
method public abstract android.hardware.camera2.CaptureRequest.Builder createCaptureRequest(int) throws android.hardware.camera2.CameraAccessException;
method public abstract void createCaptureSession(java.util.List<android.view.Surface>, android.hardware.camera2.CameraCaptureSession.StateCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
+ method public abstract android.hardware.camera2.CaptureRequest.Builder createReprocessCaptureRequest(android.hardware.camera2.TotalCaptureResult) throws android.hardware.camera2.CameraAccessException;
+ method public abstract void createReprocessibleCaptureSession(android.hardware.camera2.params.InputConfiguration, java.util.List<android.view.Surface>, android.hardware.camera2.CameraCaptureSession.StateCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
method public abstract java.lang.String getId();
field public static final int TEMPLATE_MANUAL = 6; // 0x6
field public static final int TEMPLATE_PREVIEW = 1; // 0x1
@@ -13709,6 +13716,7 @@
method public int describeContents();
method public T get(android.hardware.camera2.CaptureRequest.Key<T>);
method public java.lang.Object getTag();
+ method public boolean isReprocess();
method public void writeToParcel(android.os.Parcel, int);
field public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Boolean> BLACK_LEVEL_LOCK;
field public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Integer> COLOR_CORRECTION_ABERRATION_MODE;
@@ -13913,6 +13921,13 @@
field public static final int SCORE_MIN = 1; // 0x1
}
+ public final class InputConfiguration {
+ ctor public InputConfiguration(int, int, int);
+ method public int getFormat();
+ method public int getHeight();
+ method public int getWidth();
+ }
+
public final class LensShadingMap {
method public void copyGainFactors(float[], int);
method public int getColumnCount();
@@ -30761,7 +30776,7 @@
field public static final int START_WITH_SCREENSHOT = 2; // 0x2
}
- public abstract class VoiceInteractionSession implements android.view.KeyEvent.Callback {
+ public abstract class VoiceInteractionSession implements android.content.ComponentCallbacks2 android.view.KeyEvent.Callback {
ctor public VoiceInteractionSession(android.content.Context);
ctor public VoiceInteractionSession(android.content.Context, android.os.Handler);
method public void finish();
@@ -30777,6 +30792,7 @@
method public abstract void onCommand(android.service.voice.VoiceInteractionSession.Caller, android.service.voice.VoiceInteractionSession.Request, java.lang.String, android.os.Bundle);
method public void onCompleteVoice(android.service.voice.VoiceInteractionSession.Caller, android.service.voice.VoiceInteractionSession.Request, java.lang.CharSequence, android.os.Bundle);
method public void onComputeInsets(android.service.voice.VoiceInteractionSession.Insets);
+ method public void onConfigurationChanged(android.content.res.Configuration);
method public abstract void onConfirm(android.service.voice.VoiceInteractionSession.Caller, android.service.voice.VoiceInteractionSession.Request, java.lang.CharSequence, android.os.Bundle);
method public void onCreate(android.os.Bundle, int);
method public android.view.View onCreateContentView();
@@ -30789,10 +30805,12 @@
method public boolean onKeyLongPress(int, android.view.KeyEvent);
method public boolean onKeyMultiple(int, int, android.view.KeyEvent);
method public boolean onKeyUp(int, android.view.KeyEvent);
+ method public void onLowMemory();
method public abstract void onPickOption(android.service.voice.VoiceInteractionSession.Caller, android.service.voice.VoiceInteractionSession.Request, java.lang.CharSequence, android.app.VoiceInteractor.PickOptionRequest.Option[], android.os.Bundle);
method public void onShow(android.os.Bundle, int);
method public void onTaskFinished(android.content.Intent, int);
method public void onTaskStarted(android.content.Intent, int);
+ method public void onTrimMemory(int);
method public void setContentView(android.view.View);
method public void setKeepAwake(boolean);
method public void setTheme(int);
@@ -38190,6 +38208,8 @@
field public static final int TEXT_ALIGNMENT_VIEW_START = 5; // 0x5
field public static final int TEXT_DIRECTION_ANY_RTL = 2; // 0x2
field public static final int TEXT_DIRECTION_FIRST_STRONG = 1; // 0x1
+ field public static final int TEXT_DIRECTION_FIRST_STRONG_LTR = 6; // 0x6
+ field public static final int TEXT_DIRECTION_FIRST_STRONG_RTL = 7; // 0x7
field public static final int TEXT_DIRECTION_INHERIT = 0; // 0x0
field public static final int TEXT_DIRECTION_LOCALE = 5; // 0x5
field public static final int TEXT_DIRECTION_LTR = 3; // 0x3
diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java
index 908d46e..fa28143 100644
--- a/cmds/am/src/com/android/commands/am/Am.java
+++ b/cmds/am/src/com/android/commands/am/Am.java
@@ -124,6 +124,7 @@
" am restart\n" +
" am idle-maintenance\n" +
" am screen-compat [on|off] <PACKAGE>\n" +
+ " am package-importance <PACKAGE>\n" +
" am to-uri [INTENT]\n" +
" am to-intent-uri [INTENT]\n" +
" am to-app-uri [INTENT]\n" +
@@ -233,6 +234,8 @@
"\n" +
"am screen-compat: control screen compatibility mode of <PACKAGE>.\n" +
"\n" +
+ "am package-importance: print current importance of <PACKAGE>.\n" +
+ "\n" +
"am to-uri: print the given Intent specification as a URI.\n" +
"\n" +
"am to-intent-uri: print the given Intent specification as an intent: URI.\n" +
@@ -365,6 +368,8 @@
runIdleMaintenance();
} else if (op.equals("screen-compat")) {
runScreenCompat();
+ } else if (op.equals("package-importance")) {
+ runPackageImportance();
} else if (op.equals("to-uri")) {
runToUri(0);
} else if (op.equals("to-intent-uri")) {
@@ -1604,6 +1609,16 @@
} while (packageName != null);
}
+ private void runPackageImportance() throws Exception {
+ String packageName = nextArgRequired();
+ try {
+ int procState = mAm.getPackageProcessState(packageName);
+ System.out.println(
+ ActivityManager.RunningAppProcessInfo.procStateToImportance(procState));
+ } catch (RemoteException e) {
+ }
+ }
+
private void runToUri(int flags) throws Exception {
Intent intent = makeIntent(UserHandle.USER_CURRENT);
System.out.println(intent.toUri(flags));
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 2b35cd4..51ececc 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -2010,27 +2010,47 @@
public int lastTrimLevel;
/**
- * Constant for {@link #importance}: this process is running the
- * foreground UI.
+ * Constant for {@link #importance}: This process is running the
+ * foreground UI; that is, it is the thing currently at the top of the screen
+ * that the user is interacting with.
*/
public static final int IMPORTANCE_FOREGROUND = 100;
/**
- * Constant for {@link #importance}: this process is running something
+ * Constant for {@link #importance}: This process is running a foreground
+ * service, for example to perform music playback even while the user is
+ * not immediately in the app. This generally indicates that the process
+ * is doing something the user actively cares about.
+ */
+ public static final int IMPORTANCE_FOREGROUND_SERVICE = 125;
+
+ /**
+ * Constant for {@link #importance}: This process is running the foreground
+ * UI, but the device is asleep so it is not visible to the user. This means
+ * the user is not really aware of the process, because they can not see or
+ * interact with it, but it is quite important because it what they expect to
+ * return to once unlocking the device.
+ */
+ public static final int IMPORTANCE_TOP_SLEEPING = 150;
+
+ /**
+ * Constant for {@link #importance}: This process is running something
* that is actively visible to the user, though not in the immediate
- * foreground.
+ * foreground. This may be running a window that is behind the current
+ * foreground (so paused and with its state saved, not interacting with
+ * the user, but visible to them to some degree); it may also be running
+ * other services under the system's control that it inconsiders important.
*/
public static final int IMPORTANCE_VISIBLE = 200;
/**
- * Constant for {@link #importance}: this process is running something
- * that is considered to be actively perceptible to the user. An
- * example would be an application performing background music playback.
+ * Constant for {@link #importance}: This process is not something the user
+ * is directly aware of, but is otherwise perceptable to them to some degree.
*/
public static final int IMPORTANCE_PERCEPTIBLE = 130;
/**
- * Constant for {@link #importance}: this process is running an
+ * Constant for {@link #importance}: This process is running an
* application that can not save its state, and thus can't be killed
* while in the background.
* @hide
@@ -2038,42 +2058,51 @@
public static final int IMPORTANCE_CANT_SAVE_STATE = 170;
/**
- * Constant for {@link #importance}: this process is contains services
- * that should remain running.
+ * Constant for {@link #importance}: This process is contains services
+ * that should remain running. These are background services apps have
+ * started, not something the user is aware of, so they may be killed by
+ * the system relatively freely (though it is generally desired that they
+ * stay running as long as they want to).
*/
public static final int IMPORTANCE_SERVICE = 300;
/**
- * Constant for {@link #importance}: this process process contains
+ * Constant for {@link #importance}: This process process contains
* background code that is expendable.
*/
public static final int IMPORTANCE_BACKGROUND = 400;
/**
- * Constant for {@link #importance}: this process is empty of any
+ * Constant for {@link #importance}: This process is empty of any
* actively running code.
*/
public static final int IMPORTANCE_EMPTY = 500;
/**
- * Constant for {@link #importance}: this process does not exist.
+ * Constant for {@link #importance}: This process does not exist.
*/
public static final int IMPORTANCE_GONE = 1000;
/** @hide */
public static int procStateToImportance(int procState) {
- if (procState >= ActivityManager.PROCESS_STATE_HOME) {
- return ActivityManager.RunningAppProcessInfo.IMPORTANCE_BACKGROUND;
- } else if (procState >= ActivityManager.PROCESS_STATE_SERVICE) {
- return ActivityManager.RunningAppProcessInfo.IMPORTANCE_SERVICE;
- } else if (procState > ActivityManager.PROCESS_STATE_HEAVY_WEIGHT) {
- 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_FOREGROUND_SERVICE) {
- return ActivityManager.RunningAppProcessInfo.IMPORTANCE_VISIBLE;
+ if (procState == PROCESS_STATE_NONEXISTENT) {
+ return IMPORTANCE_GONE;
+ } else if (procState >= PROCESS_STATE_HOME) {
+ return IMPORTANCE_BACKGROUND;
+ } else if (procState >= PROCESS_STATE_SERVICE) {
+ return IMPORTANCE_SERVICE;
+ } else if (procState > PROCESS_STATE_HEAVY_WEIGHT) {
+ return IMPORTANCE_CANT_SAVE_STATE;
+ } else if (procState >= PROCESS_STATE_IMPORTANT_BACKGROUND) {
+ return IMPORTANCE_PERCEPTIBLE;
+ } else if (procState >= PROCESS_STATE_IMPORTANT_FOREGROUND) {
+ return IMPORTANCE_VISIBLE;
+ } else if (procState >= PROCESS_STATE_TOP_SLEEPING) {
+ return IMPORTANCE_TOP_SLEEPING;
+ } else if (procState >= PROCESS_STATE_FOREGROUND_SERVICE) {
+ return IMPORTANCE_FOREGROUND_SERVICE;
} else {
- return ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND;
+ return IMPORTANCE_FOREGROUND;
}
}
@@ -2253,6 +2282,22 @@
}
/**
+ * Return the importance of a given package name, based on the processes that are
+ * currently running. The return value is one of the importance constants defined
+ * in {@link RunningAppProcessInfo}, giving you the highest importance of all the
+ * processes that this package has code running inside of. If there are no processes
+ * running its code, {@link RunningAppProcessInfo#IMPORTANCE_GONE} is returned.
+ */
+ public int getPackageImportance(String packageName) {
+ try {
+ int procState = ActivityManagerNative.getDefault().getPackageProcessState(packageName);
+ return RunningAppProcessInfo.procStateToImportance(procState);
+ } catch (RemoteException e) {
+ return RunningAppProcessInfo.IMPORTANCE_GONE;
+ }
+ }
+
+ /**
* Return global memory state information for the calling process. This
* does not fill in all fields of the {@link RunningAppProcessInfo}. The
* only fields that will be filled in are
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index f63d13c..256d87d 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -106,9 +106,24 @@
}
}
- static public void noteWakeupAlarm(PendingIntent ps, int sourceUid, String sourcePkg) {
+ static public void noteWakeupAlarm(PendingIntent ps, int sourceUid, String sourcePkg,
+ String tag) {
try {
- getDefault().noteWakeupAlarm(ps.getTarget(), sourceUid, sourcePkg);
+ getDefault().noteWakeupAlarm(ps.getTarget(), sourceUid, sourcePkg, tag);
+ } catch (RemoteException ex) {
+ }
+ }
+
+ static public void noteAlarmStart(PendingIntent ps, int sourceUid, String tag) {
+ try {
+ getDefault().noteAlarmStart(ps.getTarget(), sourceUid, tag);
+ } catch (RemoteException ex) {
+ }
+ }
+
+ static public void noteAlarmFinish(PendingIntent ps, int sourceUid, String tag) {
+ try {
+ getDefault().noteAlarmFinish(ps.getTarget(), sourceUid, tag);
} catch (RemoteException ex) {
}
}
@@ -1375,7 +1390,30 @@
data.readStrongBinder());
int sourceUid = data.readInt();
String sourcePkg = data.readString();
- noteWakeupAlarm(is, sourceUid, sourcePkg);
+ String tag = data.readString();
+ noteWakeupAlarm(is, sourceUid, sourcePkg, tag);
+ reply.writeNoException();
+ return true;
+ }
+
+ case NOTE_ALARM_START_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ IIntentSender is = IIntentSender.Stub.asInterface(
+ data.readStrongBinder());
+ int sourceUid = data.readInt();
+ String tag = data.readString();
+ noteAlarmStart(is, sourceUid, tag);
+ reply.writeNoException();
+ return true;
+ }
+
+ case NOTE_ALARM_FINISH_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ IIntentSender is = IIntentSender.Stub.asInterface(
+ data.readStrongBinder());
+ int sourceUid = data.readInt();
+ String tag = data.readString();
+ noteAlarmFinish(is, sourceUid, tag);
reply.writeNoException();
return true;
}
@@ -2461,6 +2499,15 @@
reply.writeNoException();
return true;
}
+
+ case GET_PACKAGE_PROCESS_STATE_TRANSACTION: {
+ data.enforceInterface(IActivityManager.descriptor);
+ String pkg = data.readString();
+ int res = getPackageProcessState(pkg);
+ reply.writeNoException();
+ reply.writeInt(res);
+ return true;
+ }
}
return super.onTransact(code, data, reply, flags);
@@ -3304,7 +3351,7 @@
data.writeInterfaceToken(IActivityManager.descriptor);
data.writeInt(stackBoxId);
r.writeToParcel(data, 0);
- mRemote.transact(RESIZE_STACK_TRANSACTION, data, reply, IBinder.FLAG_ONEWAY);
+ mRemote.transact(RESIZE_STACK_TRANSACTION, data, reply, 0);
reply.readException();
data.recycle();
reply.recycle();
@@ -3360,7 +3407,7 @@
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
data.writeInt(stackId);
- mRemote.transact(SET_FOCUSED_STACK_TRANSACTION, data, reply, IBinder.FLAG_ONEWAY);
+ mRemote.transact(SET_FOCUSED_STACK_TRANSACTION, data, reply, 0);
reply.readException();
data.recycle();
reply.recycle();
@@ -3384,7 +3431,7 @@
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
data.writeStrongBinder(listener.asBinder());
- mRemote.transact(REGISTER_TASK_STACK_LISTENER_TRANSACTION, data, reply, IBinder.FLAG_ONEWAY);
+ mRemote.transact(REGISTER_TASK_STACK_LISTENER_TRANSACTION, data, reply, 0);
reply.readException();
data.recycle();
reply.recycle();
@@ -4214,16 +4261,37 @@
mRemote.transact(ENTER_SAFE_MODE_TRANSACTION, data, null, 0);
data.recycle();
}
- public void noteWakeupAlarm(IIntentSender sender, int sourceUid, String sourcePkg)
+ public void noteWakeupAlarm(IIntentSender sender, int sourceUid, String sourcePkg, String tag)
throws RemoteException {
Parcel data = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
data.writeStrongBinder(sender.asBinder());
data.writeInt(sourceUid);
data.writeString(sourcePkg);
+ data.writeString(tag);
mRemote.transact(NOTE_WAKEUP_ALARM_TRANSACTION, data, null, 0);
data.recycle();
}
+ public void noteAlarmStart(IIntentSender sender, int sourceUid, String tag)
+ throws RemoteException {
+ Parcel data = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeStrongBinder(sender.asBinder());
+ data.writeInt(sourceUid);
+ data.writeString(tag);
+ mRemote.transact(NOTE_ALARM_START_TRANSACTION, data, null, 0);
+ data.recycle();
+ }
+ public void noteAlarmFinish(IIntentSender sender, int sourceUid, String tag)
+ throws RemoteException {
+ Parcel data = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeStrongBinder(sender.asBinder());
+ data.writeInt(sourceUid);
+ data.writeString(tag);
+ mRemote.transact(NOTE_ALARM_FINISH_TRANSACTION, data, null, 0);
+ data.recycle();
+ }
public boolean killPids(int[] pids, String reason, boolean secure) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
@@ -5486,7 +5554,7 @@
data.writeInterfaceToken(IActivityManager.descriptor);
data.writeStrongBinder(token);
values.writeToParcel(data, 0);
- mRemote.transact(SET_TASK_DESCRIPTION_TRANSACTION, data, reply, IBinder.FLAG_ONEWAY);
+ mRemote.transact(SET_TASK_DESCRIPTION_TRANSACTION, data, reply, 0);
reply.readException();
data.recycle();
reply.recycle();
@@ -5499,7 +5567,7 @@
data.writeInterfaceToken(IActivityManager.descriptor);
data.writeInt(taskId);
data.writeInt(resizeable ? 1 : 0);
- mRemote.transact(SET_TASK_RESIZEABLE_TRANSACTION, data, reply, IBinder.FLAG_ONEWAY);
+ mRemote.transact(SET_TASK_RESIZEABLE_TRANSACTION, data, reply, 0);
reply.readException();
data.recycle();
reply.recycle();
@@ -5513,7 +5581,7 @@
data.writeInterfaceToken(IActivityManager.descriptor);
data.writeInt(taskId);
r.writeToParcel(data, 0);
- mRemote.transact(RESIZE_TASK_TRANSACTION, data, reply, IBinder.FLAG_ONEWAY);
+ mRemote.transact(RESIZE_TASK_TRANSACTION, data, reply, 0);
reply.readException();
data.recycle();
reply.recycle();
@@ -5545,7 +5613,7 @@
data.writeInt(1);
data.writeBundle(options.toBundle());
}
- mRemote.transact(START_IN_PLACE_ANIMATION_TRANSACTION, data, reply, IBinder.FLAG_ONEWAY);
+ mRemote.transact(START_IN_PLACE_ANIMATION_TRANSACTION, data, reply, 0);
reply.readException();
data.recycle();
reply.recycle();
@@ -5586,8 +5654,7 @@
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
data.writeStrongBinder(token);
- mRemote.transact(BACKGROUND_RESOURCES_RELEASED_TRANSACTION, data, reply,
- IBinder.FLAG_ONEWAY);
+ mRemote.transact(BACKGROUND_RESOURCES_RELEASED_TRANSACTION, data, reply, 0);
reply.readException();
data.recycle();
reply.recycle();
@@ -5599,8 +5666,7 @@
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
data.writeStrongBinder(token);
- mRemote.transact(NOTIFY_LAUNCH_TASK_BEHIND_COMPLETE_TRANSACTION, data, reply,
- IBinder.FLAG_ONEWAY);
+ mRemote.transact(NOTIFY_LAUNCH_TASK_BEHIND_COMPLETE_TRANSACTION, data, reply, 0);
reply.readException();
data.recycle();
reply.recycle();
@@ -5612,8 +5678,7 @@
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
data.writeStrongBinder(token);
- mRemote.transact(NOTIFY_ENTER_ANIMATION_COMPLETE_TRANSACTION, data, reply,
- IBinder.FLAG_ONEWAY);
+ mRemote.transact(NOTIFY_ENTER_ANIMATION_COMPLETE_TRANSACTION, data, reply, 0);
reply.readException();
data.recycle();
reply.recycle();
@@ -5703,11 +5768,25 @@
data.writeInterfaceToken(IActivityManager.descriptor);
data.writeInt(userId);
data.writeStringArray(packages);
- mRemote.transact(UPDATE_LOCK_TASK_PACKAGES_TRANSACTION, data, reply, IBinder.FLAG_ONEWAY);
+ mRemote.transact(UPDATE_LOCK_TASK_PACKAGES_TRANSACTION, data, reply, 0);
reply.readException();
data.recycle();
reply.recycle();
}
+ @Override
+ public int getPackageProcessState(String packageName) throws RemoteException {
+ Parcel data = Parcel.obtain();
+ Parcel reply = Parcel.obtain();
+ data.writeInterfaceToken(IActivityManager.descriptor);
+ data.writeString(packageName);
+ mRemote.transact(GET_PACKAGE_PROCESS_STATE_TRANSACTION, data, reply, 0);
+ reply.readException();
+ int res = reply.readInt();
+ data.recycle();
+ reply.recycle();
+ return res;
+ }
+
private IBinder mRemote;
}
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 4a1d6ff..59de281 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -262,7 +262,11 @@
public void enterSafeMode() throws RemoteException;
- public void noteWakeupAlarm(IIntentSender sender, int sourceUid, String sourcePkg)
+ public void noteWakeupAlarm(IIntentSender sender, int sourceUid, String sourcePkg, String tag)
+ throws RemoteException;
+ public void noteAlarmStart(IIntentSender sender, int sourceUid, String tag)
+ throws RemoteException;
+ public void noteAlarmFinish(IIntentSender sender, int sourceUid, String tag)
throws RemoteException;
public boolean killPids(int[] pids, String reason, boolean secure) throws RemoteException;
@@ -490,6 +494,8 @@
throws RemoteException;
public void updateLockTaskPackages(int userId, String[] packages) throws RemoteException;
+ public int getPackageProcessState(String packageName) throws RemoteException;
+
/*
* Private non-Binder interfaces
*/
@@ -825,4 +831,7 @@
int DUMP_HEAP_FINISHED_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+288;
int SET_VOICE_KEEP_AWAKE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+289;
int UPDATE_LOCK_TASK_PACKAGES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+290;
+ int NOTE_ALARM_START_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+291;
+ int NOTE_ALARM_FINISH_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+292;
+ int GET_PACKAGE_PROCESS_STATE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+293;
}
diff --git a/core/java/android/hardware/camera2/CameraCaptureSession.java b/core/java/android/hardware/camera2/CameraCaptureSession.java
index ce83028..6b6f026 100644
--- a/core/java/android/hardware/camera2/CameraCaptureSession.java
+++ b/core/java/android/hardware/camera2/CameraCaptureSession.java
@@ -17,21 +17,31 @@
package android.hardware.camera2;
import android.os.Handler;
+import android.view.Surface;
import java.util.List;
+
/**
- * A configured capture session for a {@link CameraDevice}, used for capturing
- * images from the camera.
+ * A configured capture session for a {@link CameraDevice}, used for capturing images from the
+ * camera or reprocessing images captured from the camera in the same session previously.
*
* <p>A CameraCaptureSession is created by providing a set of target output surfaces to
- * {@link CameraDevice#createCaptureSession createCaptureSession}. Once created, the session is
- * active until a new session is created by the camera device, or the camera device is closed.</p>
+ * {@link CameraDevice#createCaptureSession createCaptureSession}, or by providing an
+ * {@link android.hardware.camera2.params.InputConfiguration} and a set of target output surfaces to
+ * {@link CameraDevice#createReprocessibleCaptureSession createReprocessibleCaptureSession} for a
+ * reprocessible capture session. Once created, the session is active until a new session is
+ * created by the camera device, or the camera device is closed.</p>
+ *
+ * <p>All capture sessions can be used for capturing images from the camera but only reprocessible
+ * capture sessions can reprocess images captured from the camera in the same session previously.
+ * </p>
*
* <p>Creating a session is an expensive operation and can take several hundred milliseconds, since
* it requires configuring the camera device's internal pipelines and allocating memory buffers for
* sending images to the desired targets. Therefore the setup is done asynchronously, and
- * {@link CameraDevice#createCaptureSession createCaptureSession} will send the ready-to-use
- * CameraCaptureSession to the provided listener's
+ * {@link CameraDevice#createCaptureSession createCaptureSession} and
+ * {@link CameraDevice#createReprocessibleCaptureSession createReprocessibleCaptureSession} will
+ * send the ready-to-use CameraCaptureSession to the provided listener's
* {@link CameraCaptureSession.StateCallback#onConfigured onConfigured} callback. If configuration
* cannot be completed, then the
* {@link CameraCaptureSession.StateCallback#onConfigureFailed onConfigureFailed} is called, and the
@@ -77,6 +87,12 @@
* {@link #setRepeatingBurst}, and will be processed as soon as the current
* repeat/repeatBurst processing completes.</p>
*
+ * <p>All capture sessions can be used for capturing images from the camera but only capture
+ * sessions created by
+ * {@link CameraDevice#createReprocessibleCaptureSession createReprocessibleCaptureSession}
+ * can submit reprocess capture requests. Submitting a reprocess request to a regular capture
+ * session will result in an {@link IllegalArgumentException}.</p>
+ *
* @param request the settings for this capture
* @param listener The callback object to notify once this request has been
* processed. If null, no metadata will be produced for this capture,
@@ -94,7 +110,9 @@
* was explicitly closed, a new session has been created
* or the camera device has been closed.
* @throws IllegalArgumentException if the request targets no Surfaces or Surfaces that are not
- * configured as outputs for this session. Or if the handler is
+ * configured as outputs for this session. Or if a reprocess
+ * capture request is submitted in a non-reprocessible capture
+ * session. Or if the handler is
* null, the listener is not null, and the calling thread has
* no looper.
*
@@ -102,6 +120,7 @@
* @see #setRepeatingRequest
* @see #setRepeatingBurst
* @see #abortCaptures
+ * @see CameraDevice#createReprocessibleCaptureSession
*/
public abstract int capture(CaptureRequest request, CaptureCallback listener, Handler handler)
throws CameraAccessException;
@@ -121,6 +140,13 @@
* {@link #capture} repeatedly is that this method guarantees that no
* other requests will be interspersed with the burst.</p>
*
+ * <p>All capture sessions can be used for capturing images from the camera but only capture
+ * sessions created by
+ * {@link CameraDevice#createReprocessibleCaptureSession createReprocessibleCaptureSession}
+ * can submit reprocess capture requests. The list of requests must all be capturing images from
+ * the camera or all be reprocess capture requests. Submitting a reprocess request to a regular
+ * capture session will result in an {@link IllegalArgumentException}.</p>
+ *
* @param requests the list of settings for this burst capture
* @param listener The callback object to notify each time one of the
* requests in the burst has been processed. If null, no metadata will be
@@ -138,9 +164,13 @@
* @throws IllegalStateException if this session is no longer active, either because the session
* was explicitly closed, a new session has been created
* or the camera device has been closed.
- * @throws IllegalArgumentException If the requests target no Surfaces or Surfaces not currently
- * configured as outputs. Or if the handler is null, the
- * listener is not null, and the calling thread has no looper.
+ * @throws IllegalArgumentException If the requests target no Surfaces, or target Surfaces not
+ * currently configured as outputs. Or if a reprocess
+ * capture request is submitted in a non-reprocessible capture
+ * session. Or if the list of requests contains both requests
+ * to capture images from the camera and reprocess capture
+ * requests. Or if the handler is null, the listener is not
+ * null, and the calling thread has no looper.
*
* @see #capture
* @see #setRepeatingRequest
@@ -175,6 +205,14 @@
* in-progress burst will be completed before the new repeat request will be
* used.</p>
*
+ * <p>This method does not support reprocess capture requests because each reprocess
+ * {@link CaptureRequest} must be created from the {@link TotalCaptureResult} that matches
+ * the input image to be reprocessed. This is either the {@link TotalCaptureResult} of capture
+ * that is sent for reprocessing, or one of the {@link TotalCaptureResult TotalCaptureResults}
+ * of a set of captures, when data from the whole set is combined by the application into a
+ * single reprocess input image. The request must be capturing images from the camera. If a
+ * reprocess capture request is submitted, this method will throw IllegalArgumentException.</p>
+ *
* @param request the request to repeat indefinitely
* @param listener The callback object to notify every time the
* request finishes processing. If null, no metadata will be
@@ -193,9 +231,10 @@
* was explicitly closed, a new session has been created
* or the camera device has been closed.
* @throws IllegalArgumentException If the requests reference no Surfaces or Surfaces that are
- * not currently configured as outputs. Or if the handler is
- * null, the listener is not null, and the calling thread has
- * no looper. Or if no requests were passed in.
+ * not currently configured as outputs. Or if the request is
+ * a reprocess capture request. Or if the handler is null, the
+ * listener is not null, and the calling thread has no looper.
+ * Or if no requests were passed in.
*
* @see #capture
* @see #captureBurst
@@ -235,6 +274,14 @@
* in-progress burst will be completed before the new repeat burst will be
* used.</p>
*
+ * <p>This method does not support reprocess capture requests because each reprocess
+ * {@link CaptureRequest} must be created from the {@link TotalCaptureResult} that matches
+ * the input image to be reprocessed. This is either the {@link TotalCaptureResult} of capture
+ * that is sent for reprocessing, or one of the {@link TotalCaptureResult TotalCaptureResults}
+ * of a set of captures, when data from the whole set is combined by the application into a
+ * single reprocess input image. The request must be capturing images from the camera. If a
+ * reprocess capture request is submitted, this method will throw IllegalArgumentException.</p>
+ *
* @param requests the list of requests to cycle through indefinitely
* @param listener The callback object to notify each time one of the
* requests in the repeating bursts has finished processing. If null, no
@@ -253,7 +300,8 @@
* was explicitly closed, a new session has been created
* or the camera device has been closed.
* @throws IllegalArgumentException If the requests reference no Surfaces or Surfaces not
- * currently configured as outputs. Or if the handler is null,
+ * currently configured as outputs. Or if one of the requests
+ * is a reprocess capture request. Or if the handler is null,
* the listener is not null, and the calling thread has no
* looper. Or if no requests were passed in.
*
@@ -298,9 +346,10 @@
* request or a repeating burst is set, it will be cleared.</p>
*
* <p>This method is the fastest way to switch the camera device to a new session with
- * {@link CameraDevice#createCaptureSession}, at the cost of discarding in-progress work. It
- * must be called before the new session is created. Once all pending requests are either
- * completed or thrown away, the {@link StateCallback#onReady} callback will be called,
+ * {@link CameraDevice#createCaptureSession} or
+ * {@link CameraDevice#createReprocessibleCaptureSession}, at the cost of discarding in-progress
+ * work. It must be called before the new session is created. Once all pending requests are
+ * either completed or thrown away, the {@link StateCallback#onReady} callback will be called,
* if the session has not been closed. Otherwise, the {@link StateCallback#onClosed}
* callback will be fired when a new session is created by the camera device.</p>
*
@@ -321,10 +370,39 @@
* @see #setRepeatingRequest
* @see #setRepeatingBurst
* @see CameraDevice#createCaptureSession
+ * @see CameraDevice#createReprocessibleCaptureSession
*/
public abstract void abortCaptures() throws CameraAccessException;
/**
+ * Return if the application can submit reprocess capture requests with this camera capture
+ * session.
+ *
+ * @return {@code true} if the application can submit reprocess capture requests with this
+ * camera capture session. {@code false} otherwise.
+ *
+ * @see CameraDevice#createReprocessibleCaptureSession
+ */
+ public abstract boolean isReprocessible();
+
+ /**
+ * Get the input Surface associated with a reprocessible capture session.
+ *
+ * <p>Each reprocessible capture session has an input {@link Surface} where the reprocess
+ * capture requests get the input images from, rather than the camera device. The application
+ * can create a {@link android.media.ImageWriter} with this input {@link Surface} and use it to
+ * provide input images for reprocess capture requests.</p>
+ *
+ * @return The {@link Surface} where reprocessing capture requests get the input images from. If
+ * this is not a reprocess capture session, {@code null} will be returned.
+ *
+ * @see CameraDevice#createReprocessibleCaptureSession
+ * @see android.media.ImageWriter
+ * @see android.media.ImageReader
+ */
+ public abstract Surface getInputSurface();
+
+ /**
* Close this capture session asynchronously.
*
* <p>Closing a session frees up the target output Surfaces of the session for reuse with either
diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java
index fd4cf3c..51b326b 100644
--- a/core/java/android/hardware/camera2/CameraDevice.java
+++ b/core/java/android/hardware/camera2/CameraDevice.java
@@ -16,6 +16,7 @@
package android.hardware.camera2;
+import android.hardware.camera2.params.InputConfiguration;
import android.hardware.camera2.params.StreamConfigurationMap;
import android.hardware.camera2.params.OutputConfiguration;
import android.os.Handler;
@@ -135,7 +136,7 @@
*
* <p>The active capture session determines the set of potential output Surfaces for
* the camera device for each capture request. A given request may use all
- * or a only some of the outputs. Once the CameraCaptureSession is created, requests can be
+ * or only some of the outputs. Once the CameraCaptureSession is created, requests can be
* can be submitted with {@link CameraCaptureSession#capture capture},
* {@link CameraCaptureSession#captureBurst captureBurst},
* {@link CameraCaptureSession#setRepeatingRequest setRepeatingRequest}, or
@@ -393,6 +394,75 @@
List<OutputConfiguration> outputConfigurations,
CameraCaptureSession.StateCallback callback, Handler handler)
throws CameraAccessException;
+ /**
+ * Create a new reprocessible camera capture session by providing the desired reprocessing
+ * input Surface configuration and the target output set of Surfaces to the camera device.
+ *
+ * <p>If a camera device supports YUV reprocessing
+ * ({@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING}) or OPAQUE
+ * reprocessing
+ * ({@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES_OPAQUE_REPROCESSING}), besides
+ * the capture session created via {@link #createCaptureSession}, the application can also
+ * create a reprocessible capture session to submit reprocess capture requests in addition to
+ * regular capture requests. A reprocess capture request takes the next available buffer from
+ * the session's input Surface, and sends it through the camera device's processing pipeline
+ * again, to produce buffers for the request's target output Surfaces. No new image data is
+ * captured for a reprocess request. However the input buffer provided by
+ * the application must be captured previously by the same camera device in the same session
+ * directly (e.g. for Zero-Shutter-Lag use case) or indirectly (e.g. combining multiple output
+ * images).</p>
+ *
+ * <p>The active reprocessible capture session determines an input {@link Surface} and the set
+ * of potential output Surfaces for the camera devices for each capture request. The application
+ * can use {@link #createCaptureRequest} to create regular capture requests to capture new
+ * images from the camera device, and use {@link #createReprocessCaptureRequest} to create
+ * reprocess capture requests to process buffers from the input {@link Surface}. A request may
+ * use all or only some of the outputs. All the output Surfaces in one capture request will come
+ * from the same source, either from a new capture by the camera device, or from the input
+ * Surface depending on if the request is a reprocess capture request.</p>
+ *
+ * <p>Input formats and sizes supported by the camera device can be queried via
+ * {@link StreamConfigurationMap#getInputFormats} and
+ * {@link StreamConfigurationMap#getInputSizes}. For each supported input format, the camera
+ * device supports a set of output formats and sizes for reprocessing that can be queried via
+ * {@link StreamConfigurationMap#getValidOutputFormatsForInput} and
+ * {@link StreamConfigurationMap#getOutputSizes}. While output Surfaces with formats that
+ * aren't valid reprocess output targets for the input configuration can be part of a session,
+ * they cannot be used as targets for a reprocessing request.</p>
+ *
+ * <p>Since the application cannot access {@link android.graphics.ImageFormat#PRIVATE} images
+ * directly, an output Surface created by {@link android.media.ImageReader#newOpaqueInstance}
+ * will be considered as intended to be used for reprocessing input and thus the
+ * {@link android.media.ImageReader} size must match one of the supported input sizes for
+ * {@link android.graphics.ImageFormat#PRIVATE} format. Otherwise, creating a reprocessible
+ * capture session will fail.</p>
+ *
+ * @param inputConfig The configuration for the input {@link Surface}
+ * @param outputs The new set of Surfaces that should be made available as
+ * targets for captured image data.
+ * @param callback The callback to notify about the status of the new capture session.
+ * @param handler The handler on which the callback should be invoked, or {@code null} to use
+ * the current thread's {@link android.os.Looper looper}.
+ *
+ * @throws IllegalArgumentException if the input configuration is null or not supported, the set
+ * of output Surfaces do not meet the requirements, the
+ * callback is null, or the handler is null but the current
+ * thread has no looper.
+ * @throws CameraAccessException if the camera device is no longer connected or has
+ * encountered a fatal error
+ * @throws IllegalStateException if the camera device has been closed
+ *
+ * @see CameraCaptureSession
+ * @see StreamConfigurationMap#getInputFormats
+ * @see StreamConfigurationMap#getInputSizes
+ * @see StreamConfigurationMap#getValidOutputFormatsForInput
+ * @see StreamConfigurationMap#getOutputSizes
+ * @see android.media.ImageWriter
+ * @see android.media.ImageReader
+ */
+ public abstract void createReprocessibleCaptureSession(InputConfiguration inputConfig,
+ List<Surface> outputs, CameraCaptureSession.StateCallback callback, Handler handler)
+ throws CameraAccessException;
/**
* <p>Create a {@link CaptureRequest.Builder} for new capture requests,
@@ -423,6 +493,36 @@
throws CameraAccessException;
/**
+ * <p>Create a {@link CaptureRequest.Builder} for a new reprocess {@link CaptureRequest} from a
+ * {@link TotalCaptureResult}.
+ *
+ * <p>Each reprocess {@link CaptureRequest} processes one buffer from
+ * {@link CameraCaptureSession}'s input {@link Surface} to all output {@link Surface Surfaces}
+ * included in the reprocess capture request. The reprocess input images must be generated from
+ * one or multiple output images captured from the same camera device. The application can
+ * provide input images to camera device via
+ * {{@link android.media.ImageWriter#queueInputImage ImageWriter#queueInputImage}}.
+ * The application must use the capture result of one of those output images to create a
+ * reprocess capture request so that the camera device can use the information to achieve
+ * optimal reprocess image quality.
+ *
+ * @param inputResult The capture result of the output image or one of the output images used
+ * to generate the reprocess input image for this capture request.
+ *
+ * @throws IllegalArgumentException if inputResult is null.
+ * @throws CameraAccessException if the camera device is no longer connected or has
+ * encountered a fatal error
+ * @throws IllegalStateException if the camera device has been closed
+ *
+ * @see CaptureRequest.Builder
+ * @see TotalCaptureResult
+ * @see CameraDevice#createReprocessibleCaptureSession
+ * @see android.media.ImageWriter
+ */
+ public abstract CaptureRequest.Builder createReprocessCaptureRequest(
+ TotalCaptureResult inputResult) throws CameraAccessException;
+
+ /**
* Close the connection to this camera device as quickly as possible.
*
* <p>Immediately after this call, all calls to the camera device or active session interface
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index b513379..1a00a05 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -148,7 +148,7 @@
* new one provided.</p>
*
* <p>The first time a callback is registered, it is immediately called
- * with the torch mode status of all currently known camera devices.</p>
+ * with the torch mode status of all currently known camera devices with a flash unit.</p>
*
* <p>Since this callback will be registered with the camera service, remember to unregister it
* once it is no longer needed; otherwise the callback will continue to receive events
@@ -524,7 +524,7 @@
* A callback for camera flash torch modes becoming unavailable, disabled, or enabled.
*
* <p>The torch mode becomes unavailable when the camera device it belongs to becomes
- * unavailable or other camera resouces it needs become busy due to other higher priority
+ * unavailable or other camera resources it needs become busy due to other higher priority
* camera activities. The torch mode becomes disabled when it was turned off or when the camera
* device it belongs to is no longer in use and other camera resources it needs are no longer
* busy. A camera's torch mode is turned off when an application calls {@link #setTorchMode} to
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index b8fb8e7..35727e8 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -157,6 +157,7 @@
private final HashSet<Surface> mSurfaceSet;
private final CameraMetadataNative mSettings;
+ private boolean mIsReprocess;
private Object mUserTag;
@@ -168,6 +169,7 @@
private CaptureRequest() {
mSettings = new CameraMetadataNative();
mSurfaceSet = new HashSet<Surface>();
+ mIsReprocess = false;
}
/**
@@ -179,6 +181,7 @@
private CaptureRequest(CaptureRequest source) {
mSettings = new CameraMetadataNative(source.mSettings);
mSurfaceSet = (HashSet<Surface>) source.mSurfaceSet.clone();
+ mIsReprocess = source.mIsReprocess;
mUserTag = source.mUserTag;
}
@@ -187,9 +190,10 @@
*
* Used by the Builder to create a mutable CaptureRequest.
*/
- private CaptureRequest(CameraMetadataNative settings) {
+ private CaptureRequest(CameraMetadataNative settings, boolean isReprocess) {
mSettings = CameraMetadataNative.move(settings);
mSurfaceSet = new HashSet<Surface>();
+ mIsReprocess = isReprocess;
}
/**
@@ -257,10 +261,27 @@
}
/**
+ * Determine if this is a reprocess capture request.
+ *
+ * <p>A reprocess capture request produces output images from an input buffer from the
+ * {@link CameraCaptureSession}'s input {@link Surface}. A reprocess capture request can be
+ * created by {@link CameraDevice#createReprocessCaptureRequest}.</p>
+ *
+ * @return {@code true} if this is a reprocess capture request. {@code false} if this is not a
+ * reprocess capture request.
+ *
+ * @see CameraDevice#createReprocessCaptureRequest
+ */
+ public boolean isReprocess() {
+ return mIsReprocess;
+ }
+
+ /**
* Determine whether this CaptureRequest is equal to another CaptureRequest.
*
* <p>A request is considered equal to another is if it's set of key/values is equal, it's
- * list of output surfaces is equal, and the user tag is equal.</p>
+ * list of output surfaces is equal, the user tag is equal, and the return values of
+ * isReprocess() are equal.</p>
*
* @param other Another instance of CaptureRequest.
*
@@ -276,7 +297,8 @@
return other != null
&& Objects.equals(mUserTag, other.mUserTag)
&& mSurfaceSet.equals(other.mSurfaceSet)
- && mSettings.equals(other.mSettings);
+ && mSettings.equals(other.mSettings)
+ && mIsReprocess == other.mIsReprocess;
}
@Override
@@ -323,6 +345,8 @@
Surface s = (Surface) p;
mSurfaceSet.add(s);
}
+
+ mIsReprocess = (in.readInt() == 0) ? false : true;
}
@Override
@@ -334,6 +358,7 @@
public void writeToParcel(Parcel dest, int flags) {
mSettings.writeToParcel(dest, flags);
dest.writeParcelableArray(mSurfaceSet.toArray(new Surface[mSurfaceSet.size()]), flags);
+ dest.writeInt(mIsReprocess ? 1 : 0);
}
/**
@@ -374,8 +399,8 @@
*
* @hide
*/
- public Builder(CameraMetadataNative template) {
- mRequest = new CaptureRequest(template);
+ public Builder(CameraMetadataNative template, boolean reprocess) {
+ mRequest = new CaptureRequest(template, reprocess);
}
/**
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index d8f92e5..4134d28 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -295,11 +295,18 @@
* <p>Whenever a request has been processed, regardless of failure or success,
* it gets a unique frame number assigned to its future result/failure.</p>
*
- * <p>This value monotonically increments, starting with 0,
- * for every new result or failure; and the scope is the lifetime of the
- * {@link CameraDevice}.</p>
+ * <p>For the same type of request (capturing from the camera device or reprocessing), this
+ * value monotonically increments, starting with 0, for every new result or failure and the
+ * scope is the lifetime of the {@link CameraDevice}. Between different types of requests,
+ * the frame number may not monotonically increment. For example, the frame number of a newer
+ * reprocess result may be smaller than the frame number of an older result of capturing new
+ * images from the camera device, but the frame number of a newer reprocess result will never be
+ * smaller than the frame number of an older reprocess result.</p>
*
* @return The frame number
+ *
+ * @see CameraDevice#createCaptureRequest
+ * @see CameraDevice#createReprocessCaptureRequest
*/
public long getFrameNumber() {
return mFrameNumber;
diff --git a/core/java/android/hardware/camera2/ICameraDeviceUser.aidl b/core/java/android/hardware/camera2/ICameraDeviceUser.aidl
index 01f2396..23bfa66 100644
--- a/core/java/android/hardware/camera2/ICameraDeviceUser.aidl
+++ b/core/java/android/hardware/camera2/ICameraDeviceUser.aidl
@@ -16,11 +16,11 @@
package android.hardware.camera2;
-import android.hardware.camera2.params.OutputConfiguration;
-import android.hardware.camera2.impl.CameraMetadataNative;
import android.hardware.camera2.CaptureRequest;
-
+import android.hardware.camera2.impl.CameraMetadataNative;
+import android.hardware.camera2.params.OutputConfiguration;
import android.hardware.camera2.utils.LongParcelable;
+import android.view.Surface;
/** @hide */
interface ICameraDeviceUser
@@ -68,6 +68,29 @@
// non-negative value is the stream ID. negative value is status_t
int createStream(in OutputConfiguration outputConfiguration);
+ /**
+ * Create an input stream
+ *
+ * <p>Create an input stream of width, height, and format</p>
+ *
+ * @param width Width of the input buffers
+ * @param height Height of the input buffers
+ * @param format Format of the input buffers. One of HAL_PIXEL_FORMAT_*.
+ *
+ * @return stream ID if it's a non-negative value. status_t if it's a negative value.
+ */
+ int createInputStream(int width, int height, int format);
+
+ /**
+ * Get the surface of the input stream.
+ *
+ * <p>It's valid to call this method only after a stream configuration is completed
+ * successfully and the stream configuration includes a input stream.</p>
+ *
+ * @param surface An output argument for the surface of the input stream buffer queue.
+ */
+ int getInputSurface(out Surface surface);
+
int createDefaultRequest(int templateId, out CameraMetadataNative request);
int getCameraInfo(out CameraMetadataNative info);
diff --git a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
index e87a2f8..fb5b13c 100644
--- a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
@@ -44,6 +44,8 @@
private final int mId;
private final String mIdString;
+ /** Input surface configured by native camera framework based on user-specified configuration */
+ private final Surface mInput;
/** User-specified set of surfaces used as the configuration outputs */
private final List<Surface> mOutputs;
/**
@@ -85,7 +87,7 @@
* There must be no pending actions
* (e.g. no pending captures, no repeating requests, no flush).</p>
*/
- CameraCaptureSessionImpl(int id, List<Surface> outputs,
+ CameraCaptureSessionImpl(int id, Surface input, List<Surface> outputs,
CameraCaptureSession.StateCallback callback, Handler stateHandler,
android.hardware.camera2.impl.CameraDeviceImpl deviceImpl,
Handler deviceStateHandler, boolean configureSuccess) {
@@ -100,6 +102,7 @@
// TODO: extra verification of outputs
mOutputs = outputs;
+ mInput = input;
mStateHandler = checkHandler(stateHandler);
mStateCallback = createUserStateCallbackProxy(mStateHandler, callback);
@@ -145,8 +148,12 @@
Handler handler) throws CameraAccessException {
if (request == null) {
throw new IllegalArgumentException("request must not be null");
+ } else if (request.isReprocess() && !isReprocessible()) {
+ throw new IllegalArgumentException("this capture session cannot handle reprocess " +
+ "requests");
}
+
checkNotClosed();
handler = checkHandler(handler, callback);
@@ -169,6 +176,19 @@
throw new IllegalArgumentException("requests must have at least one element");
}
+ boolean reprocess = requests.get(0).isReprocess();
+ if (reprocess && !isReprocessible()) {
+ throw new IllegalArgumentException("this capture session cannot handle reprocess " +
+ "requests");
+ }
+
+ for (int i = 1; i < requests.size(); i++) {
+ if (requests.get(i).isReprocess() != reprocess) {
+ throw new IllegalArgumentException("cannot mix regular and reprocess capture " +
+ " requests");
+ }
+ }
+
checkNotClosed();
handler = checkHandler(handler, callback);
@@ -188,8 +208,11 @@
Handler handler) throws CameraAccessException {
if (request == null) {
throw new IllegalArgumentException("request must not be null");
+ } else if (request.isReprocess()) {
+ throw new IllegalArgumentException("repeating reprocess requests are not supported");
}
+
checkNotClosed();
handler = checkHandler(handler, callback);
@@ -212,6 +235,13 @@
throw new IllegalArgumentException("requests must have at least one element");
}
+ for (CaptureRequest r : requests) {
+ if (r.isReprocess()) {
+ throw new IllegalArgumentException("repeating reprocess burst requests are not " +
+ "supported");
+ }
+ }
+
checkNotClosed();
handler = checkHandler(handler, callback);
@@ -257,6 +287,16 @@
// The next BUSY -> IDLE set of transitions will mark the end of the abort.
}
+ @Override
+ public boolean isReprocessible() {
+ return mInput != null;
+ }
+
+ @Override
+ public Surface getInputSurface() {
+ return mInput;
+ }
+
/**
* Replace this session with another session.
*
@@ -658,8 +698,8 @@
mUnconfigureDrainer.taskStarted();
try {
- mDeviceImpl
- .configureOutputsChecked(null); // begin transition to unconfigured
+ // begin transition to unconfigured
+ mDeviceImpl.configureStreamsChecked(null, null);
} catch (CameraAccessException e) {
// OK: do not throw checked exceptions.
Log.e(TAG, mIdString + "Exception while configuring outputs: ", e);
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index 38f8e39..91388c3 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -28,7 +28,10 @@
import android.hardware.camera2.ICameraDeviceCallbacks;
import android.hardware.camera2.ICameraDeviceUser;
import android.hardware.camera2.TotalCaptureResult;
+import android.hardware.camera2.params.InputConfiguration;
import android.hardware.camera2.params.OutputConfiguration;
+import android.hardware.camera2.params.ReprocessFormatsMap;
+import android.hardware.camera2.params.StreamConfigurationMap;
import android.hardware.camera2.utils.CameraBinderDecorator;
import android.hardware.camera2.utils.CameraRuntimeException;
import android.hardware.camera2.utils.LongParcelable;
@@ -37,6 +40,7 @@
import android.os.Looper;
import android.os.RemoteException;
import android.util.Log;
+import android.util.Size;
import android.util.SparseArray;
import android.view.Surface;
@@ -46,7 +50,8 @@
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
-import java.util.TreeSet;
+import java.util.LinkedList;
+import java.util.TreeMap;
/**
* HAL2.1+ implementation of CameraDevice. Use CameraManager#open to instantiate
@@ -78,9 +83,11 @@
private int mRepeatingRequestId = REQUEST_ID_NONE;
private final ArrayList<Integer> mRepeatingRequestIdDeletedList = new ArrayList<Integer>();
- // Map stream IDs to Surfaces
+ // Map stream IDs to input/output configurations
+ private SimpleEntry<Integer, InputConfiguration> mConfiguredInput =
+ new SimpleEntry<>(REQUEST_ID_NONE, null);
private final SparseArray<OutputConfiguration> mConfiguredOutputs =
- new SparseArray<OutputConfiguration>();
+ new SparseArray<>();
private final String mCameraId;
private final CameraCharacteristics mCharacteristics;
@@ -320,38 +327,48 @@
for (Surface s : outputs) {
outputConfigs.add(new OutputConfiguration(s));
}
- configureOutputsChecked(outputConfigs);
+ configureStreamsChecked(/*inputConfig*/null, outputConfigs);
+
}
/**
- * Attempt to configure the outputs; the device goes to idle and then configures the
- * new outputs if possible.
+ * Attempt to configure the input and outputs; the device goes to idle and then configures the
+ * new input and outputs if possible.
*
- * <p>The configuration may gracefully fail, if there are too many outputs, if the formats
- * are not supported, or if the sizes for that format is not supported. In this case this
- * function will return {@code false} and the unconfigured callback will be fired.</p>
+ * <p>The configuration may gracefully fail, if input configuration is not supported,
+ * if there are too many outputs, if the formats are not supported, or if the sizes for that
+ * format is not supported. In this case this function will return {@code false} and the
+ * unconfigured callback will be fired.</p>
*
- * <p>If the configuration succeeds (with 1 or more outputs), then the idle callback is fired.
- * Unconfiguring the device always fires the idle callback.</p>
+ * <p>If the configuration succeeds (with 1 or more outputs with or without an input),
+ * then the idle callback is fired. Unconfiguring the device always fires the idle callback.</p>
*
+ * @param inputConfig input configuration or {@code null} for no input
* @param outputs a list of one or more surfaces, or {@code null} to unconfigure
* @return whether or not the configuration was successful
*
* @throws CameraAccessException if there were any unexpected problems during configuration
*/
- public boolean configureOutputsChecked(List<OutputConfiguration> outputs)
- throws CameraAccessException {
+ public boolean configureStreamsChecked(InputConfiguration inputConfig,
+ List<OutputConfiguration> outputs) throws CameraAccessException {
// Treat a null input the same an empty list
if (outputs == null) {
outputs = new ArrayList<OutputConfiguration>();
}
+ if (outputs.size() == 0 && inputConfig != null) {
+ throw new IllegalArgumentException("cannot configure an input stream without " +
+ "any output streams");
+ }
+
+ checkInputConfiguration(inputConfig);
+
boolean success = false;
synchronized(mInterfaceLock) {
checkIfCameraClosedOrInError();
// Streams to create
HashSet<OutputConfiguration> addSet = new HashSet<OutputConfiguration>(outputs);
- // Streams to delete
+ // Streams to delete
List<Integer> deleteList = new ArrayList<Integer>();
// Determine which streams need to be created, which to be deleted
@@ -373,6 +390,24 @@
waitUntilIdle();
mRemoteDevice.beginConfigure();
+
+ // reconfigure the input stream if the input configuration is different.
+ InputConfiguration currentInputConfig = mConfiguredInput.getValue();
+ if (inputConfig != currentInputConfig &&
+ (inputConfig == null || !inputConfig.equals(currentInputConfig))) {
+ if (currentInputConfig != null) {
+ mRemoteDevice.deleteStream(mConfiguredInput.getKey());
+ mConfiguredInput = new SimpleEntry<Integer, InputConfiguration>(
+ REQUEST_ID_NONE, null);
+ }
+ if (inputConfig != null) {
+ int streamId = mRemoteDevice.createInputStream(inputConfig.getWidth(),
+ inputConfig.getHeight(), inputConfig.getFormat());
+ mConfiguredInput = new SimpleEntry<Integer, InputConfiguration>(
+ streamId, inputConfig);
+ }
+ }
+
// Delete all streams first (to free up HW resources)
for (Integer streamId : deleteList) {
mRemoteDevice.deleteStream(streamId);
@@ -429,7 +464,7 @@
for (Surface surface : outputs) {
outConfigurations.add(new OutputConfiguration(surface));
}
- createCaptureSessionByOutputConfiguration(outConfigurations, callback, handler);
+ createCaptureSessionInternal(null, outConfigurations, callback, handler);
}
@Override
@@ -437,9 +472,39 @@
List<OutputConfiguration> outputConfigurations,
CameraCaptureSession.StateCallback callback, Handler handler)
throws CameraAccessException {
+ if (DEBUG) {
+ Log.d(TAG, "createCaptureSessionByOutputConfiguration");
+ }
+
+ createCaptureSessionInternal(null, outputConfigurations, callback, handler);
+ }
+
+ @Override
+ public void createReprocessibleCaptureSession(InputConfiguration inputConfig,
+ List<Surface> outputs, CameraCaptureSession.StateCallback callback, Handler handler)
+ throws CameraAccessException {
+ if (DEBUG) {
+ Log.d(TAG, "createReprocessibleCaptureSession");
+ }
+
+ if (inputConfig == null) {
+ throw new IllegalArgumentException("inputConfig cannot be null when creating a " +
+ "reprocessible capture session");
+ }
+ List<OutputConfiguration> outConfigurations = new ArrayList<>(outputs.size());
+ for (Surface surface : outputs) {
+ outConfigurations.add(new OutputConfiguration(surface));
+ }
+ createCaptureSessionInternal(inputConfig, outConfigurations, callback, handler);
+ }
+
+ private void createCaptureSessionInternal(InputConfiguration inputConfig,
+ List<OutputConfiguration> outputConfigurations,
+ CameraCaptureSession.StateCallback callback, Handler handler)
+ throws CameraAccessException {
synchronized(mInterfaceLock) {
if (DEBUG) {
- Log.d(TAG, "createCaptureSession");
+ Log.d(TAG, "createCaptureSessionInternal");
}
checkIfCameraClosedOrInError();
@@ -453,15 +518,24 @@
// TODO: dont block for this
boolean configureSuccess = true;
CameraAccessException pendingException = null;
+ Surface input = null;
try {
- // configure outputs and then block until IDLE
- configureSuccess = configureOutputsChecked(outputConfigurations);
+ // configure streams and then block until IDLE
+ configureSuccess = configureStreamsChecked(inputConfig, outputConfigurations);
+ if (inputConfig != null) {
+ input = new Surface();
+ mRemoteDevice.getInputSurface(/*out*/input);
+ }
} catch (CameraAccessException e) {
configureSuccess = false;
pendingException = e;
+ input = null;
if (DEBUG) {
Log.v(TAG, "createCaptureSession - failed with exception ", e);
}
+ } catch (RemoteException e) {
+ // impossible
+ return;
}
List<Surface> outSurfaces = new ArrayList<>(outputConfigurations.size());
@@ -470,7 +544,7 @@
}
// Fire onConfigured if configureOutputs succeeded, fire onConfigureFailed otherwise.
CameraCaptureSessionImpl newSession =
- new CameraCaptureSessionImpl(mNextSessionId++,
+ new CameraCaptureSessionImpl(mNextSessionId++, input,
outSurfaces, callback, handler, this, mDeviceHandler,
configureSuccess);
@@ -512,12 +586,25 @@
}
CaptureRequest.Builder builder =
- new CaptureRequest.Builder(templatedRequest);
+ new CaptureRequest.Builder(templatedRequest, /*reprocess*/false);
return builder;
}
}
+ @Override
+ public CaptureRequest.Builder createReprocessCaptureRequest(TotalCaptureResult inputResult)
+ throws CameraAccessException {
+ synchronized(mInterfaceLock) {
+ checkIfCameraClosedOrInError();
+
+ CameraMetadataNative resultMetadata = new
+ CameraMetadataNative(inputResult.getNativeCopy());
+
+ return new CaptureRequest.Builder(resultMetadata, /*reprocess*/true);
+ }
+ }
+
public int capture(CaptureRequest request, CaptureCallback callback, Handler handler)
throws CameraAccessException {
if (DEBUG) {
@@ -810,6 +897,40 @@
}
}
+ private void checkInputConfiguration(InputConfiguration inputConfig) {
+ if (inputConfig != null) {
+ StreamConfigurationMap configMap = mCharacteristics.get(
+ CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
+
+ int[] inputFormats = configMap.getInputFormats();
+ boolean validFormat = false;
+ for (int format : inputFormats) {
+ if (format == inputConfig.getFormat()) {
+ validFormat = true;
+ }
+ }
+
+ if (validFormat == false) {
+ throw new IllegalArgumentException("input format " + inputConfig.getFormat() +
+ " is not valid");
+ }
+
+ boolean validSize = false;
+ Size[] inputSizes = configMap.getInputSizes(inputConfig.getFormat());
+ for (Size s : inputSizes) {
+ if (inputConfig.getWidth() == s.getWidth() &&
+ inputConfig.getHeight() == s.getHeight()) {
+ validSize = true;
+ }
+ }
+
+ if (validSize == false) {
+ throw new IllegalArgumentException("input size " + inputConfig.getWidth() + "x" +
+ inputConfig.getHeight() + " is not valid");
+ }
+ }
+ }
+
/**
* <p>A callback for tracking the progress of a {@link CaptureRequest}
* submitted to the camera device.</p>
@@ -996,19 +1117,46 @@
public class FrameNumberTracker {
private long mCompletedFrameNumber = -1;
- private final TreeSet<Long> mFutureErrorSet = new TreeSet<Long>();
+ private long mCompletedReprocessFrameNumber = -1;
+ /** the skipped frame numbers that belong to regular results */
+ private final LinkedList<Long> mSkippedRegularFrameNumbers = new LinkedList<Long>();
+ /** the skipped frame numbers that belong to reprocess results */
+ private final LinkedList<Long> mSkippedReprocessFrameNumbers = new LinkedList<Long>();
+ /** frame number -> is reprocess */
+ private final TreeMap<Long, Boolean> mFutureErrorMap = new TreeMap<Long, Boolean>();
/** Map frame numbers to list of partial results */
private final HashMap<Long, List<CaptureResult>> mPartialResults = new HashMap<>();
private void update() {
- Iterator<Long> iter = mFutureErrorSet.iterator();
+ Iterator iter = mFutureErrorMap.entrySet().iterator();
while (iter.hasNext()) {
- long errorFrameNumber = iter.next();
- if (errorFrameNumber == mCompletedFrameNumber + 1) {
- mCompletedFrameNumber++;
- iter.remove();
+ TreeMap.Entry pair = (TreeMap.Entry)iter.next();
+ Long errorFrameNumber = (Long)pair.getKey();
+ Boolean reprocess = (Boolean)pair.getValue();
+ Boolean removeError = true;
+ if (reprocess) {
+ if (errorFrameNumber == mCompletedReprocessFrameNumber + 1) {
+ mCompletedReprocessFrameNumber = errorFrameNumber;
+ } else if (mSkippedReprocessFrameNumbers.isEmpty() != true &&
+ errorFrameNumber == mSkippedReprocessFrameNumbers.element()) {
+ mCompletedReprocessFrameNumber = errorFrameNumber;
+ mSkippedReprocessFrameNumbers.remove();
+ } else {
+ removeError = false;
+ }
} else {
- break;
+ if (errorFrameNumber == mCompletedFrameNumber + 1) {
+ mCompletedFrameNumber = errorFrameNumber;
+ } else if (mSkippedRegularFrameNumbers.isEmpty() != true &&
+ errorFrameNumber == mSkippedRegularFrameNumbers.element()) {
+ mCompletedFrameNumber = errorFrameNumber;
+ mSkippedRegularFrameNumbers.remove();
+ } else {
+ removeError = false;
+ }
+ }
+ if (removeError) {
+ iter.remove();
}
}
}
@@ -1017,25 +1165,21 @@
* This function is called every time when a result or an error is received.
* @param frameNumber the frame number corresponding to the result or error
* @param isError true if it is an error, false if it is not an error
+ * @param isReprocess true if it is a reprocess result, false if it is a regular result.
*/
- public void updateTracker(long frameNumber, boolean isError) {
+ public void updateTracker(long frameNumber, boolean isError, boolean isReprocess) {
if (isError) {
- mFutureErrorSet.add(frameNumber);
+ mFutureErrorMap.put(frameNumber, isReprocess);
} else {
- /**
- * HAL cannot send an OnResultReceived for frame N unless it knows for
- * sure that all frames prior to N have either errored out or completed.
- * So if the current frame is not an error, then all previous frames
- * should have arrived. The following line checks whether this holds.
- */
- if (frameNumber != mCompletedFrameNumber + 1) {
- Log.e(TAG, String.format(
- "result frame number %d comes out of order, should be %d + 1",
- frameNumber, mCompletedFrameNumber));
- // Continue on to set the completed frame number to this frame anyway,
- // to be robust to lower-level errors and allow for clean shutdowns.
+ try {
+ if (isReprocess) {
+ updateCompletedReprocessFrameNumber(frameNumber);
+ } else {
+ updateCompletedFrameNumber(frameNumber);
+ }
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, e.getMessage());
}
- mCompletedFrameNumber = frameNumber;
}
update();
}
@@ -1049,12 +1193,13 @@
* @param frameNumber the frame number corresponding to the result
* @param result the total or partial result
* @param partial {@true} if the result is partial, {@code false} if total
+ * @param isReprocess true if it is a reprocess result, false if it is a regular result.
*/
- public void updateTracker(long frameNumber, CaptureResult result, boolean partial) {
-
+ public void updateTracker(long frameNumber, CaptureResult result, boolean partial,
+ boolean isReprocess) {
if (!partial) {
// Update the total result's frame status as being successful
- updateTracker(frameNumber, /*isError*/false);
+ updateTracker(frameNumber, /*isError*/false, isReprocess);
// Don't keep a list of total results, we don't need to track them
return;
}
@@ -1093,28 +1238,112 @@
return mCompletedFrameNumber;
}
+ public long getCompletedReprocessFrameNumber() {
+ return mCompletedReprocessFrameNumber;
+ }
+
+ /**
+ * Update the completed frame number for regular results.
+ *
+ * It validates that all previous frames have arrived except for reprocess frames.
+ *
+ * If there is a gap since previous regular frame number, assume the frames in the gap are
+ * reprocess frames and store them in the skipped reprocess frame number queue to check
+ * against when reprocess frames arrive.
+ */
+ private void updateCompletedFrameNumber(long frameNumber) throws IllegalArgumentException {
+ if (frameNumber <= mCompletedFrameNumber) {
+ throw new IllegalArgumentException("frame number " + frameNumber + " is a repeat");
+ } else if (frameNumber <= mCompletedReprocessFrameNumber) {
+ // if frame number is smaller than completed reprocess frame number,
+ // it must be the head of mSkippedRegularFrameNumbers
+ if (mSkippedRegularFrameNumbers.isEmpty() == true ||
+ frameNumber < mSkippedRegularFrameNumbers.element()) {
+ throw new IllegalArgumentException("frame number " + frameNumber +
+ " is a repeat");
+ } else if (frameNumber > mSkippedRegularFrameNumbers.element()) {
+ throw new IllegalArgumentException("frame number " + frameNumber +
+ " comes out of order. Expecting " +
+ mSkippedRegularFrameNumbers.element());
+ }
+ // frame number matches the head of the skipped frame number queue.
+ mSkippedRegularFrameNumbers.remove();
+ } else {
+ // there is a gap of unseen frame numbers which should belong to reprocess result
+ // put all the skipped frame numbers in the queue
+ for (long i = Math.max(mCompletedFrameNumber, mCompletedReprocessFrameNumber) + 1;
+ i < frameNumber; i++) {
+ mSkippedReprocessFrameNumbers.add(i);
+ }
+ }
+
+ mCompletedFrameNumber = frameNumber;
+ }
+
+ /**
+ * Update the completed frame number for reprocess results.
+ *
+ * It validates that all previous frames have arrived except for regular frames.
+ *
+ * If there is a gap since previous reprocess frame number, assume the frames in the gap are
+ * regular frames and store them in the skipped regular frame number queue to check
+ * against when regular frames arrive.
+ */
+ private void updateCompletedReprocessFrameNumber(long frameNumber)
+ throws IllegalArgumentException {
+ if (frameNumber < mCompletedReprocessFrameNumber) {
+ throw new IllegalArgumentException("frame number " + frameNumber + " is a repeat");
+ } else if (frameNumber < mCompletedFrameNumber) {
+ // if reprocess frame number is smaller than completed regular frame number,
+ // it must be the head of the skipped reprocess frame number queue.
+ if (mSkippedReprocessFrameNumbers.isEmpty() == true ||
+ frameNumber < mSkippedReprocessFrameNumbers.element()) {
+ throw new IllegalArgumentException("frame number " + frameNumber +
+ " is a repeat");
+ } else if (frameNumber > mSkippedReprocessFrameNumbers.element()) {
+ throw new IllegalArgumentException("frame number " + frameNumber +
+ " comes out of order. Expecting " +
+ mSkippedReprocessFrameNumbers.element());
+ }
+ // frame number matches the head of the skipped frame number queue.
+ mSkippedReprocessFrameNumbers.remove();
+ } else {
+ // put all the skipped frame numbers in the queue
+ for (long i = Math.max(mCompletedFrameNumber, mCompletedReprocessFrameNumber) + 1;
+ i < frameNumber; i++) {
+ mSkippedRegularFrameNumbers.add(i);
+ }
+ }
+ mCompletedReprocessFrameNumber = frameNumber;
+ }
}
private void checkAndFireSequenceComplete() {
long completedFrameNumber = mFrameNumberTracker.getCompletedFrameNumber();
+ long completedReprocessFrameNumber = mFrameNumberTracker.getCompletedReprocessFrameNumber();
+ boolean isReprocess = false;
Iterator<SimpleEntry<Long, Integer> > iter = mFrameNumberRequestPairs.iterator();
while (iter.hasNext()) {
final SimpleEntry<Long, Integer> frameNumberRequestPair = iter.next();
- if (frameNumberRequestPair.getKey() <= completedFrameNumber) {
+ boolean sequenceCompleted = false;
+ final int requestId = frameNumberRequestPair.getValue();
+ final CaptureCallbackHolder holder;
+ synchronized(mInterfaceLock) {
+ if (mRemoteDevice == null) {
+ Log.w(TAG, "Camera closed while checking sequences");
+ return;
+ }
- // remove request from mCaptureCallbackMap
- final int requestId = frameNumberRequestPair.getValue();
- final CaptureCallbackHolder holder;
- synchronized(mInterfaceLock) {
- if (mRemoteDevice == null) {
- Log.w(TAG, "Camera closed while checking sequences");
- return;
- }
-
- int index = mCaptureCallbackMap.indexOfKey(requestId);
- holder = (index >= 0) ? mCaptureCallbackMap.valueAt(index)
- : null;
- if (holder != null) {
+ int index = mCaptureCallbackMap.indexOfKey(requestId);
+ holder = (index >= 0) ?
+ mCaptureCallbackMap.valueAt(index) : null;
+ if (holder != null) {
+ isReprocess = holder.getRequest().isReprocess();
+ // check if it's okay to remove request from mCaptureCallbackMap
+ if ((isReprocess && frameNumberRequestPair.getKey() <=
+ completedReprocessFrameNumber) || (!isReprocess &&
+ frameNumberRequestPair.getKey() <= completedFrameNumber)) {
+ sequenceCompleted = true;
mCaptureCallbackMap.removeAt(index);
if (DEBUG) {
Log.v(TAG, String.format(
@@ -1125,36 +1354,40 @@
}
}
}
+ }
+
+ // If no callback is registered for this requestId or sequence completed, remove it
+ // from the frame number->request pair because it's not needed anymore.
+ if (holder == null || sequenceCompleted) {
iter.remove();
+ }
- // Call onCaptureSequenceCompleted
- if (holder != null) {
- Runnable resultDispatch = new Runnable() {
- @Override
- public void run() {
- if (!CameraDeviceImpl.this.isClosed()){
- if (DEBUG) {
- Log.d(TAG, String.format(
- "fire sequence complete for request %d",
- requestId));
- }
-
- long lastFrameNumber = frameNumberRequestPair.getKey();
- if (lastFrameNumber < Integer.MIN_VALUE
- || lastFrameNumber > Integer.MAX_VALUE) {
- throw new AssertionError(lastFrameNumber
- + " cannot be cast to int");
- }
- holder.getCallback().onCaptureSequenceCompleted(
- CameraDeviceImpl.this,
- requestId,
- lastFrameNumber);
+ // Call onCaptureSequenceCompleted
+ if (sequenceCompleted) {
+ Runnable resultDispatch = new Runnable() {
+ @Override
+ public void run() {
+ if (!CameraDeviceImpl.this.isClosed()){
+ if (DEBUG) {
+ Log.d(TAG, String.format(
+ "fire sequence complete for request %d",
+ requestId));
}
- }
- };
- holder.getHandler().post(resultDispatch);
- }
+ long lastFrameNumber = frameNumberRequestPair.getKey();
+ if (lastFrameNumber < Integer.MIN_VALUE
+ || lastFrameNumber > Integer.MAX_VALUE) {
+ throw new AssertionError(lastFrameNumber
+ + " cannot be cast to int");
+ }
+ holder.getCallback().onCaptureSequenceCompleted(
+ CameraDeviceImpl.this,
+ requestId,
+ lastFrameNumber);
+ }
+ }
+ };
+ holder.getHandler().post(resultDispatch);
}
}
}
@@ -1319,9 +1552,11 @@
final CaptureCallbackHolder holder =
CameraDeviceImpl.this.mCaptureCallbackMap.get(requestId);
+ final CaptureRequest request = holder.getRequest(resultExtras.getSubsequenceId());
boolean isPartialResult =
(resultExtras.getPartialResultCount() < mTotalPartialCount);
+ boolean isReprocess = request.isReprocess();
// Check if we have a callback for this
if (holder == null) {
@@ -1331,7 +1566,8 @@
+ frameNumber);
}
- mFrameNumberTracker.updateTracker(frameNumber, /*result*/null, isPartialResult);
+ mFrameNumberTracker.updateTracker(frameNumber, /*result*/null, isPartialResult,
+ isReprocess);
return;
}
@@ -1343,11 +1579,11 @@
+ frameNumber);
}
- mFrameNumberTracker.updateTracker(frameNumber, /*result*/null, isPartialResult);
+ mFrameNumberTracker.updateTracker(frameNumber, /*result*/null, isPartialResult,
+ isReprocess);
return;
}
- final CaptureRequest request = holder.getRequest(resultExtras.getSubsequenceId());
Runnable resultDispatch = null;
@@ -1398,7 +1634,7 @@
holder.getHandler().post(resultDispatch);
// Collect the partials for a total result; or mark the frame as totally completed
- mFrameNumberTracker.updateTracker(frameNumber, finalResult, isPartialResult);
+ mFrameNumberTracker.updateTracker(frameNumber, finalResult, isPartialResult, isReprocess);
// Fire onCaptureSequenceCompleted
if (!isPartialResult) {
@@ -1460,7 +1696,7 @@
if (DEBUG) {
Log.v(TAG, String.format("got error frame %d", frameNumber));
}
- mFrameNumberTracker.updateTracker(frameNumber, /*error*/true);
+ mFrameNumberTracker.updateTracker(frameNumber, /*error*/true, request.isReprocess());
checkAndFireSequenceComplete();
}
diff --git a/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java b/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java
index 70f3463..4cd9414 100644
--- a/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java
+++ b/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java
@@ -530,6 +530,18 @@
}
@Override
+ public int createInputStream(int width, int height, int format) {
+ Log.e(TAG, "creating input stream is not supported on legacy devices");
+ return CameraBinderDecorator.INVALID_OPERATION;
+ }
+
+ @Override
+ public int getInputSurface(/*out*/ Surface surface) {
+ Log.e(TAG, "getting input surface is not supported on legacy devices");
+ return CameraBinderDecorator.INVALID_OPERATION;
+ }
+
+ @Override
public int createDefaultRequest(int templateId, /*out*/CameraMetadataNative request) {
if (DEBUG) {
Log.d(TAG, "createDefaultRequest called.");
diff --git a/core/java/android/hardware/camera2/params/InputConfiguration.java b/core/java/android/hardware/camera2/params/InputConfiguration.java
new file mode 100644
index 0000000..dea1c5c
--- /dev/null
+++ b/core/java/android/hardware/camera2/params/InputConfiguration.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.camera2.params;
+
+import android.hardware.camera2.utils.HashCodeHelpers;
+
+/**
+ * Immutable class to store an input configuration that is used to create a reprocessible capture
+ * session.
+ *
+ * @see CameraDevice#createReprocessibleCaptureSession
+ * @see CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP
+ */
+public final class InputConfiguration {
+
+ private final int mWidth;
+ private final int mHeight;
+ private final int mFormat;
+
+ /**
+ * Create an input configration with the width, height, and user-defined format.
+ *
+ * <p>Images of an user-defined format are accessible by applications. Use
+ * {@link android.hardware.camera2.CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP}
+ * to query supported input formats</p>
+ *
+ * @param width Width of the input buffers.
+ * @param height Height of the input buffers.
+ * @param format Format of the input buffers. One of ImageFormat or PixelFormat constants.
+ *
+ * @see android.graphics.ImageFormat
+ * @see android.graphics.PixelFormat
+ * @see android.hardware.camera2.CameraCharacteristics#SCALER_STREAM_CONFIGURATION_MAP
+ */
+ public InputConfiguration(int width, int height, int format) {
+ mWidth = width;
+ mHeight = height;
+ mFormat = format;
+ }
+
+ /**
+ * Get the width of this input configration.
+ *
+ * @return width of this input configuration.
+ */
+ public int getWidth() {
+ return mWidth;
+ }
+
+ /**
+ * Get the height of this input configration.
+ *
+ * @return height of this input configuration.
+ */
+ public int getHeight() {
+ return mHeight;
+ }
+
+ /**
+ * Get the format of this input configration.
+ *
+ * @return format of this input configuration.
+ */
+ public int getFormat() {
+ return mFormat;
+ }
+
+ /**
+ * Check if this InputConfiguration is equal to another InputConfiguration.
+ *
+ * <p>Two input configurations are equal if and only if they have the same widths, heights, and
+ * formats.</p>
+ *
+ * @param obj the object to compare this instance with.
+ *
+ * @return {@code true} if the objects were equal, {@code false} otherwise.
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof InputConfiguration)) {
+ return false;
+ }
+
+ InputConfiguration otherInputConfig = (InputConfiguration) obj;
+
+ if (otherInputConfig.getWidth() == mWidth &&
+ otherInputConfig.getHeight() == mHeight &&
+ otherInputConfig.getFormat() == mFormat) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public int hashCode() {
+ return HashCodeHelpers.hashCode(mWidth, mHeight, mFormat);
+ }
+}
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index c7edb1a..7c5ddee 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -1135,8 +1135,10 @@
public static final int EVENT_PACKAGE_INSTALLED = 0x000c;
// Event for a package being uninstalled.
public static final int EVENT_PACKAGE_UNINSTALLED = 0x000d;
+ // Event for a package being uninstalled.
+ public static final int EVENT_ALARM = 0x000e;
// Number of event types.
- public static final int EVENT_COUNT = 0x000e;
+ public static final int EVENT_COUNT = 0x000f;
// Mask to extract out only the type part of the event.
public static final int EVENT_TYPE_MASK = ~(EVENT_FLAG_START|EVENT_FLAG_FINISH);
@@ -1158,6 +1160,8 @@
EVENT_USER_FOREGROUND | EVENT_FLAG_START;
public static final int EVENT_USER_FOREGROUND_FINISH =
EVENT_USER_FOREGROUND | EVENT_FLAG_FINISH;
+ public static final int EVENT_ALARM_START = EVENT_ALARM | EVENT_FLAG_START;
+ public static final int EVENT_ALARM_FINISH = EVENT_ALARM | EVENT_FLAG_FINISH;
// For CMD_EVENT.
public int eventCode;
@@ -1789,12 +1793,12 @@
public static final String[] HISTORY_EVENT_NAMES = new String[] {
"null", "proc", "fg", "top", "sync", "wake_lock_in", "job", "user", "userfg", "conn",
- "motion", "active", "pkginst", "pkgunin"
+ "motion", "active", "pkginst", "pkgunin", "alarm"
};
public static final String[] HISTORY_EVENT_CHECKIN_NAMES = new String[] {
"Enl", "Epr", "Efg", "Etp", "Esy", "Ewl", "Ejb", "Eur", "Euf", "Ecn",
- "Esm", "Eac", "Epi", "Epu"
+ "Esm", "Eac", "Epi", "Epu", "Eal"
};
/**
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 9edf6ad..109c23b 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -7717,6 +7717,13 @@
*/
public static final String[] MULTI_SIM_USER_PREFERRED_SUBS = {"user_preferred_sub1",
"user_preferred_sub2","user_preferred_sub3"};
+
+ /**
+ * Whether to enable new contacts aggregator or not.
+ * The value 1 - enable, 0 - disable
+ * @hide
+ */
+ public static final String NEW_CONTACT_AGGREGATOR = "new_contact_aggregator";
}
/**
diff --git a/core/java/android/service/voice/VoiceInteractionSession.java b/core/java/android/service/voice/VoiceInteractionSession.java
index 20d7079..71b7f76 100644
--- a/core/java/android/service/voice/VoiceInteractionSession.java
+++ b/core/java/android/service/voice/VoiceInteractionSession.java
@@ -20,8 +20,10 @@
import android.app.Dialog;
import android.app.Instrumentation;
import android.app.VoiceInteractor;
+import android.content.ComponentCallbacks2;
import android.content.Context;
import android.content.Intent;
+import android.content.res.Configuration;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Rect;
@@ -65,7 +67,8 @@
* when done. It can also initiate voice interactions with applications by calling
* {@link #startVoiceActivity}</p>.
*/
-public abstract class VoiceInteractionSession implements KeyEvent.Callback {
+public abstract class VoiceInteractionSession implements KeyEvent.Callback,
+ ComponentCallbacks2 {
static final String TAG = "VoiceInteractionSession";
static final boolean DEBUG = true;
@@ -855,6 +858,18 @@
hide();
}
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ }
+
+ @Override
+ public void onLowMemory() {
+ }
+
+ @Override
+ public void onTrimMemory(int level) {
+ }
+
/**
* Compute the interesting insets into your UI. The default implementation
* sets {@link Insets#contentInsets outInsets.contentInsets.top} to the height
diff --git a/core/java/android/service/voice/VoiceInteractionSessionService.java b/core/java/android/service/voice/VoiceInteractionSessionService.java
index 008d55f..8f988f3 100644
--- a/core/java/android/service/voice/VoiceInteractionSessionService.java
+++ b/core/java/android/service/voice/VoiceInteractionSessionService.java
@@ -19,6 +19,7 @@
import android.app.Service;
import android.content.Context;
import android.content.Intent;
+import android.content.res.Configuration;
import android.os.Bundle;
import android.os.IBinder;
import android.os.Looper;
@@ -76,6 +77,30 @@
return mInterface.asBinder();
}
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ if (mSession != null) {
+ mSession.onConfigurationChanged(newConfig);
+ }
+ }
+
+ @Override
+ public void onLowMemory() {
+ super.onLowMemory();
+ if (mSession != null) {
+ mSession.onLowMemory();
+ }
+ }
+
+ @Override
+ public void onTrimMemory(int level) {
+ super.onTrimMemory(level);
+ if (mSession != null) {
+ mSession.onTrimMemory(level);
+ }
+ }
+
void doNewSession(IBinder token, Bundle args, int startFlags) {
if (mSession != null) {
mSession.doDestroy();
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 3fa8c81..6b28746 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -1795,6 +1795,8 @@
* 11 PFLAG2_TEXT_DIRECTION_FLAGS[3]
* 1 PFLAG2_TEXT_DIRECTION_FLAGS[4]
* 1 1 PFLAG2_TEXT_DIRECTION_FLAGS[5]
+ * 11 PFLAG2_TEXT_DIRECTION_FLAGS[6]
+ * 111 PFLAG2_TEXT_DIRECTION_FLAGS[7]
* 111 PFLAG2_TEXT_DIRECTION_MASK
* 1 PFLAG2_TEXT_DIRECTION_RESOLVED
* 1 PFLAG2_TEXT_DIRECTION_RESOLVED_DEFAULT
@@ -1968,6 +1970,20 @@
public static final int TEXT_DIRECTION_LOCALE = 5;
/**
+ * Text direction is using "first strong algorithm". The first strong directional character
+ * determines the paragraph direction. If there is no strong directional character, the
+ * paragraph direction is LTR.
+ */
+ public static final int TEXT_DIRECTION_FIRST_STRONG_LTR = 6;
+
+ /**
+ * Text direction is using "first strong algorithm". The first strong directional character
+ * determines the paragraph direction. If there is no strong directional character, the
+ * paragraph direction is RTL.
+ */
+ public static final int TEXT_DIRECTION_FIRST_STRONG_RTL = 7;
+
+ /**
* Default text direction is inherited
*/
private static final int TEXT_DIRECTION_DEFAULT = TEXT_DIRECTION_INHERIT;
@@ -2002,7 +2018,9 @@
TEXT_DIRECTION_ANY_RTL << PFLAG2_TEXT_DIRECTION_MASK_SHIFT,
TEXT_DIRECTION_LTR << PFLAG2_TEXT_DIRECTION_MASK_SHIFT,
TEXT_DIRECTION_RTL << PFLAG2_TEXT_DIRECTION_MASK_SHIFT,
- TEXT_DIRECTION_LOCALE << PFLAG2_TEXT_DIRECTION_MASK_SHIFT
+ TEXT_DIRECTION_LOCALE << PFLAG2_TEXT_DIRECTION_MASK_SHIFT,
+ TEXT_DIRECTION_FIRST_STRONG_LTR << PFLAG2_TEXT_DIRECTION_MASK_SHIFT,
+ TEXT_DIRECTION_FIRST_STRONG_RTL << PFLAG2_TEXT_DIRECTION_MASK_SHIFT
};
/**
@@ -19636,11 +19654,13 @@
* @return the defined text direction. It can be one of:
*
* {@link #TEXT_DIRECTION_INHERIT},
- * {@link #TEXT_DIRECTION_FIRST_STRONG}
+ * {@link #TEXT_DIRECTION_FIRST_STRONG},
* {@link #TEXT_DIRECTION_ANY_RTL},
* {@link #TEXT_DIRECTION_LTR},
* {@link #TEXT_DIRECTION_RTL},
- * {@link #TEXT_DIRECTION_LOCALE}
+ * {@link #TEXT_DIRECTION_LOCALE},
+ * {@link #TEXT_DIRECTION_FIRST_STRONG_LTR},
+ * {@link #TEXT_DIRECTION_FIRST_STRONG_RTL}
*
* @attr ref android.R.styleable#View_textDirection
*
@@ -19652,7 +19672,9 @@
@ViewDebug.IntToString(from = TEXT_DIRECTION_ANY_RTL, to = "ANY_RTL"),
@ViewDebug.IntToString(from = TEXT_DIRECTION_LTR, to = "LTR"),
@ViewDebug.IntToString(from = TEXT_DIRECTION_RTL, to = "RTL"),
- @ViewDebug.IntToString(from = TEXT_DIRECTION_LOCALE, to = "LOCALE")
+ @ViewDebug.IntToString(from = TEXT_DIRECTION_LOCALE, to = "LOCALE"),
+ @ViewDebug.IntToString(from = TEXT_DIRECTION_FIRST_STRONG_LTR, to = "FIRST_STRONG_LTR"),
+ @ViewDebug.IntToString(from = TEXT_DIRECTION_FIRST_STRONG_RTL, to = "FIRST_STRONG_RTL")
})
public int getRawTextDirection() {
return (mPrivateFlags2 & PFLAG2_TEXT_DIRECTION_MASK) >> PFLAG2_TEXT_DIRECTION_MASK_SHIFT;
@@ -19664,11 +19686,13 @@
* @param textDirection the direction to set. Should be one of:
*
* {@link #TEXT_DIRECTION_INHERIT},
- * {@link #TEXT_DIRECTION_FIRST_STRONG}
+ * {@link #TEXT_DIRECTION_FIRST_STRONG},
* {@link #TEXT_DIRECTION_ANY_RTL},
* {@link #TEXT_DIRECTION_LTR},
* {@link #TEXT_DIRECTION_RTL},
* {@link #TEXT_DIRECTION_LOCALE}
+ * {@link #TEXT_DIRECTION_FIRST_STRONG_LTR},
+ * {@link #TEXT_DIRECTION_FIRST_STRONG_RTL},
*
* Resolution will be done if the value is set to TEXT_DIRECTION_INHERIT. The resolution
* proceeds up the parent chain of the view to get the value. If there is no parent, then it will
@@ -19698,11 +19722,13 @@
*
* @return the resolved text direction. Returns one of:
*
- * {@link #TEXT_DIRECTION_FIRST_STRONG}
+ * {@link #TEXT_DIRECTION_FIRST_STRONG},
* {@link #TEXT_DIRECTION_ANY_RTL},
* {@link #TEXT_DIRECTION_LTR},
* {@link #TEXT_DIRECTION_RTL},
- * {@link #TEXT_DIRECTION_LOCALE}
+ * {@link #TEXT_DIRECTION_LOCALE},
+ * {@link #TEXT_DIRECTION_FIRST_STRONG_LTR},
+ * {@link #TEXT_DIRECTION_FIRST_STRONG_RTL}
*
* @attr ref android.R.styleable#View_textDirection
*/
@@ -19712,7 +19738,9 @@
@ViewDebug.IntToString(from = TEXT_DIRECTION_ANY_RTL, to = "ANY_RTL"),
@ViewDebug.IntToString(from = TEXT_DIRECTION_LTR, to = "LTR"),
@ViewDebug.IntToString(from = TEXT_DIRECTION_RTL, to = "RTL"),
- @ViewDebug.IntToString(from = TEXT_DIRECTION_LOCALE, to = "LOCALE")
+ @ViewDebug.IntToString(from = TEXT_DIRECTION_LOCALE, to = "LOCALE"),
+ @ViewDebug.IntToString(from = TEXT_DIRECTION_FIRST_STRONG_LTR, to = "FIRST_STRONG_LTR"),
+ @ViewDebug.IntToString(from = TEXT_DIRECTION_FIRST_STRONG_RTL, to = "FIRST_STRONG_RTL")
})
public int getTextDirection() {
return (mPrivateFlags2 & PFLAG2_TEXT_DIRECTION_RESOLVED_MASK) >> PFLAG2_TEXT_DIRECTION_RESOLVED_MASK_SHIFT;
@@ -19771,6 +19799,8 @@
case TEXT_DIRECTION_LTR:
case TEXT_DIRECTION_RTL:
case TEXT_DIRECTION_LOCALE:
+ case TEXT_DIRECTION_FIRST_STRONG_LTR:
+ case TEXT_DIRECTION_FIRST_STRONG_RTL:
mPrivateFlags2 |=
(parentResolvedDirection << PFLAG2_TEXT_DIRECTION_RESOLVED_MASK_SHIFT);
break;
@@ -19784,6 +19814,8 @@
case TEXT_DIRECTION_LTR:
case TEXT_DIRECTION_RTL:
case TEXT_DIRECTION_LOCALE:
+ case TEXT_DIRECTION_FIRST_STRONG_LTR:
+ case TEXT_DIRECTION_FIRST_STRONG_RTL:
// Resolved direction is the same as text direction
mPrivateFlags2 |= (textDirection << PFLAG2_TEXT_DIRECTION_RESOLVED_MASK_SHIFT);
break;
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 29073be..1be05f3 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -3370,7 +3370,7 @@
// Parent's (TextView) previous position in window
private int mLastParentX, mLastParentY;
// Previous text character offset
- private int mPreviousOffset = -1;
+ protected int mPreviousOffset = -1;
// Previous text character offset
private boolean mPositionHasChanged = true;
// Minimum touch target size for handles
@@ -3830,8 +3830,6 @@
}
private class SelectionStartHandleView extends HandleView {
- // The previous offset this handle was at.
- private int mPrevOffset;
// Indicates whether the cursor is making adjustments within a word.
private boolean mInWord = false;
// Offset to track difference between touch and word boundary.
@@ -3879,7 +3877,7 @@
int end = getWordEnd(offset, true);
int start = getWordStart(offset);
- if (offset < mPrevOffset) {
+ if (offset < mPreviousOffset) {
// User is increasing the selection.
if (!mInWord || currLine < mPrevLine) {
// We're not in a word, or we're on a different line so we'll expand by
@@ -3888,21 +3886,19 @@
if (offset <= end - offsetToWord || currLine < mPrevLine) {
offset = start;
} else {
- offset = mPrevOffset;
+ offset = mPreviousOffset;
}
}
- mPrevOffset = offset;
mTouchWordOffset = Math.max(trueOffset - offset, 0);
mInWord = !isStartBoundary(offset);
positionCursor = true;
- } else if (offset - mTouchWordOffset > mPrevOffset) {
+ } else if (offset - mTouchWordOffset > mPreviousOffset) {
// User is shrinking the selection.
if (currLine > mPrevLine) {
// We're on a different line, so we'll snap to word boundaries.
offset = end;
}
offset -= mTouchWordOffset;
- mPrevOffset = offset;
mInWord = !isEndBoundary(offset);
positionCursor = true;
}
@@ -3936,8 +3932,6 @@
}
private class SelectionEndHandleView extends HandleView {
- // The previous offset this handle was at.
- private int mPrevOffset;
// Indicates whether the cursor is making adjustments within a word.
private boolean mInWord = false;
// Offset to track difference between touch and word boundary.
@@ -3986,7 +3980,7 @@
int end = getWordEnd(offset, true);
int start = getWordStart(offset);
- if (offset > mPrevOffset) {
+ if (offset > mPreviousOffset) {
// User is increasing the selection.
if (!mInWord || currLine > mPrevLine) {
// We're not in a word, or we're on a different line so we'll expand by
@@ -3995,21 +3989,19 @@
if (offset >= start + midPoint || currLine > mPrevLine) {
offset = end;
} else {
- offset = mPrevOffset;
+ offset = mPreviousOffset;
}
}
- mPrevOffset = offset;
mTouchWordOffset = Math.max(offset - trueOffset, 0);
mInWord = !isEndBoundary(offset);
positionCursor = true;
- } else if (offset + mTouchWordOffset < mPrevOffset) {
+ } else if (offset + mTouchWordOffset < mPreviousOffset) {
// User is shrinking the selection.
if (currLine > mPrevLine) {
// We're on a different line, so we'll snap to word boundaries.
offset = getWordStart(offset);
}
offset += mTouchWordOffset;
- mPrevOffset = offset;
positionCursor = true;
mInWord = !isStartBoundary(offset);
}
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 9bbf375..b44a886 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -9275,6 +9275,10 @@
return TextDirectionHeuristics.RTL;
case TEXT_DIRECTION_LOCALE:
return TextDirectionHeuristics.LOCALE;
+ case TEXT_DIRECTION_FIRST_STRONG_LTR:
+ return TextDirectionHeuristics.FIRSTSTRONG_LTR;
+ case TEXT_DIRECTION_FIRST_STRONG_RTL:
+ return TextDirectionHeuristics.FIRSTSTRONG_RTL;
}
}
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index fbb2dfc..87605f6 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -2692,6 +2692,32 @@
addHistoryEventLocked(elapsedRealtime, uptime, HistoryItem.EVENT_JOB_FINISH, name, uid);
}
+ public void noteAlarmStartLocked(String name, int uid) {
+ if (!mRecordAllHistory) {
+ return;
+ }
+ uid = mapUid(uid);
+ final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
+ if (!mActiveEvents.updateState(HistoryItem.EVENT_ALARM_START, name, uid, 0)) {
+ return;
+ }
+ addHistoryEventLocked(elapsedRealtime, uptime, HistoryItem.EVENT_ALARM_START, name, uid);
+ }
+
+ public void noteAlarmFinishLocked(String name, int uid) {
+ if (!mRecordAllHistory) {
+ return;
+ }
+ uid = mapUid(uid);
+ final long elapsedRealtime = SystemClock.elapsedRealtime();
+ final long uptime = SystemClock.uptimeMillis();
+ if (!mActiveEvents.updateState(HistoryItem.EVENT_ALARM_FINISH, name, uid, 0)) {
+ return;
+ }
+ addHistoryEventLocked(elapsedRealtime, uptime, HistoryItem.EVENT_ALARM_FINISH, name, uid);
+ }
+
private void requestWakelockCpuUpdate() {
if (!mHandler.hasMessages(MSG_UPDATE_WAKELOCKS)) {
Message m = mHandler.obtainMessage(MSG_UPDATE_WAKELOCKS);
@@ -2709,6 +2735,7 @@
if (!enabled) {
// Clear out any existing state.
mActiveEvents.removeEvents(HistoryItem.EVENT_WAKE_LOCK);
+ mActiveEvents.removeEvents(HistoryItem.EVENT_ALARM);
// Record the currently running processes as stopping, now that we are no
// longer tracking them.
HashMap<String, SparseIntArray> active = mActiveEvents.getStateForEvent(
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 04e40a7..a623d0c 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -2520,6 +2520,12 @@
<enum name="rtl" value="4" />
<!-- The paragraph direction is coming from the system Locale. -->
<enum name="locale" value="5" />
+ <!-- The first strong directional character determines the paragraph direction. If
+ there is no strong directional character, the paragraph direction is LTR. -->
+ <enum name="firstStrongLtr" value="6" />
+ <!-- The first strong directional character determines the paragraph direction. If
+ there is no strong directional character, the paragraph direction is RTL. -->
+ <enum name="firstStrongRtl" value="7" />
</attr>
<!-- Defines the alignment of the text. A heuristic is used to determine the resolved
diff --git a/data/fonts/fallback_fonts.xml b/data/fonts/fallback_fonts.xml
index 5c3b90b..dc364a12 100644
--- a/data/fonts/fallback_fonts.xml
+++ b/data/fonts/fallback_fonts.xml
@@ -252,6 +252,11 @@
</family>
<family>
<fileset>
+ <file>NotoSansBamum-Regular.ttf</file>
+ </fileset>
+ </family>
+ <family>
+ <fileset>
<file>NotoSansBatak-Regular.ttf</file>
</fileset>
</family>
@@ -312,11 +317,31 @@
</family>
<family>
<fileset>
+ <file>NotoSansLisu-Regular.ttf</file>
+ </fileset>
+ </family>
+ <family>
+ <fileset>
+ <file>NotoSansMandaic-Regular.ttf</file>
+ </fileset>
+ </family>
+ <family>
+ <fileset>
<file>NotoSansMeeteiMayek-Regular.ttf</file>
</fileset>
</family>
<family>
<fileset>
+ <file>NotoSansNewTaiLue-Regular.ttf</file>
+ </fileset>
+ </family>
+ <family>
+ <fileset>
+ <file>NotoSansNKo-Regular.ttf</file>
+ </fileset>
+ </family>
+ <family>
+ <fileset>
<file>NotoSansOlChiki-Regular.ttf</file>
</fileset>
</family>
@@ -342,6 +367,11 @@
</family>
<family>
<fileset>
+ <file>NotoSansSyriacEstrangela-Regular.ttf</file>
+ </fileset>
+ </family>
+ <family>
+ <fileset>
<file>NotoSansTagbanwa-Regular.ttf</file>
</fileset>
</family>
@@ -362,6 +392,11 @@
</family>
<family>
<fileset>
+ <file>NotoSansVai-Regular.ttf</file>
+ </fileset>
+ </family>
+ <family>
+ <fileset>
<file>NotoSansYi-Regular.ttf</file>
</fileset>
</family>
@@ -411,7 +446,7 @@
</fileset>
</family>
<!--
- Noto Sans Tai Le is intentionally kept last, to make sure it doesn't override
+ Tai Le and Mongolian are intentionally kept last, to make sure they don't override
the East Asian punctuation for Chinese.
-->
<family>
@@ -419,4 +454,9 @@
<file>NotoSansTaiLe-Regular.ttf</file>
</fileset>
</family>
+ <family>
+ <fileset>
+ <file>NotoSansMongolian-Regular.ttf</file>
+ </fileset>
+ </family>
</familyset>
diff --git a/data/fonts/fonts.xml b/data/fonts/fonts.xml
index f3a7647..44ea1c9 100644
--- a/data/fonts/fonts.xml
+++ b/data/fonts/fonts.xml
@@ -239,6 +239,9 @@
<font weight="400" style="normal">NotoSansBalinese-Regular.ttf</font>
</family>
<family>
+ <font weight="400" style="normal">NotoSansBamum-Regular.ttf</font>
+ </family>
+ <family>
<font weight="400" style="normal">NotoSansBatak-Regular.ttf</font>
</family>
<family>
@@ -275,9 +278,21 @@
<font weight="400" style="normal">NotoSansLimbu-Regular.ttf</font>
</family>
<family>
+ <font weight="400" style="normal">NotoSansLisu-Regular.ttf</font>
+ </family>
+ <family>
+ <font weight="400" style="normal">NotoSansMandaic-Regular.ttf</font>
+ </family>
+ <family>
<font weight="400" style="normal">NotoSansMeeteiMayek-Regular.ttf</font>
</family>
<family>
+ <font weight="400" style="normal">NotoSansNewTaiLue-Regular.ttf</font>
+ </family>
+ <family>
+ <font weight="400" style="normal">NotoSansNKo-Regular.ttf</font>
+ </family>
+ <family>
<font weight="400" style="normal">NotoSansOlChiki-Regular.ttf</font>
</family>
<family>
@@ -293,6 +308,9 @@
<font weight="400" style="normal">NotoSansSylotiNagri-Regular.ttf</font>
</family>
<family>
+ <font weight="400" style="normal">NotoSansSyriacEstrangela-Regular.ttf</font>
+ </family>
+ <family>
<font weight="400" style="normal">NotoSansTagbanwa-Regular.ttf</font>
</family>
<family>
@@ -305,6 +323,9 @@
<font weight="400" style="normal">NotoSansTifinagh-Regular.ttf</font>
</family>
<family>
+ <font weight="400" style="normal">NotoSansVai-Regular.ttf</font>
+ </family>
+ <family>
<font weight="400" style="normal">NotoSansYi-Regular.ttf</font>
</family>
<family lang="zh-Hans">
@@ -335,10 +356,13 @@
<font weight="400" style="normal">MTLmr3m.ttf</font>
</family>
<!--
- Noto Sans Tai Le is intentionally kept last, to make sure it doesn't override
+ Tai Le and Mongolian are intentionally kept last, to make sure they don't override
the East Asian punctuation for Chinese.
-->
<family>
<font weight="400" style="normal">NotoSansTaiLe-Regular.ttf</font>
</family>
+ <family>
+ <font weight="400" style="normal">NotoSansMongolian-Regular.ttf</font>
+ </family>
</familyset>
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java
index e05e1fc..0466540 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/integration/CameraDeviceBinderTest.java
@@ -160,7 +160,7 @@
assertEquals(CameraBinderTestUtils.NO_ERROR, status);
assertFalse(metadata.isEmpty());
- CaptureRequest.Builder request = new CaptureRequest.Builder(metadata);
+ CaptureRequest.Builder request = new CaptureRequest.Builder(metadata, /*reprocess*/false);
assertFalse(request.isEmpty());
assertFalse(metadata.isEmpty());
if (needStream) {
diff --git a/packages/SystemUI/res/drawable/ic_delete.xml b/packages/SystemUI/res/drawable/ic_delete.xml
new file mode 100644
index 0000000..4e80ddf
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_delete.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24.0dp"
+ android:height="24.0dp"
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0">
+ <path
+ android:fillColor="#ffffffff"
+ android:pathData="M12 38c0 2.21 1.79 4 4 4h16c2.21 0 4,-1.79 4,-4V14H12v24zM38 8h-7l-2,-2H19l-2 2h-7v4h28V8z"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/ic_person.xml b/packages/SystemUI/res/drawable/ic_person.xml
new file mode 100644
index 0000000..101911f
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_person.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24.0dp"
+ android:height="24.0dp"
+ android:viewportWidth="48.0"
+ android:viewportHeight="48.0">
+ <path
+ android:fillColor="#ffffffff"
+ android:pathData="M24 24c4.42 0 8,-3.59 8,-8 0,-4.42,-3.58,-8,-8,-8s-8 3.58,-8 8c0 4.41 3.58 8 8 8zm0 4c-5.33 0,-16 2.67,-16 8v4h32v-4c0,-5.33,-10.67,-8,-16,-8z"/>
+</vector>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index fe974be..2858fa2 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -830,6 +830,15 @@
<!-- Notification when resuming an existing guest session: Action that continues with the current session [CHAR LIMIT=35] -->
<string name="guest_wipe_session_dontwipe">Yes, continue</string>
+ <!-- Title of the notification shown to a new guest user [CHAR LIMIT=60] -->
+ <string name="guest_notification_title">Guest user</string>
+
+ <!-- Text of the notification shown to a new guest user [CHAR LIMIT=60] -->
+ <string name="guest_notification_text">Remove guest to delete apps and data</string>
+
+ <!-- Remove action in the notification shown to a new guest user [CHAR LIMIT=30] -->
+ <string name="guest_notification_remove_action">REMOVE GUEST</string>
+
<!-- Title for add user confirmation dialog [CHAR LIMIT=30] -->
<string name="user_add_user_title" msgid="2108112641783146007">Add new user?</string>
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
index 4ac41a1..194bcfa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
@@ -19,6 +19,9 @@
import android.app.ActivityManager;
import android.app.ActivityManagerNative;
import android.app.Dialog;
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
@@ -63,6 +66,7 @@
private static final boolean DEBUG = false;
private static final String SIMPLE_USER_SWITCHER_GLOBAL_SETTING =
"lockscreenSimpleUserSwitcher";
+ private static final String ACTION_REMOVE_GUEST = "com.android.systemui.REMOVE_GUEST";
private final Context mContext;
private final UserManager mUserManager;
@@ -89,6 +93,7 @@
filter.addAction(Intent.ACTION_USER_INFO_CHANGED);
filter.addAction(Intent.ACTION_USER_SWITCHED);
filter.addAction(Intent.ACTION_USER_STOPPING);
+ filter.addAction(ACTION_REMOVE_GUEST);
mContext.registerReceiverAsUser(mReceiver, UserHandle.OWNER, filter,
null /* permission */, null /* scheduler */);
@@ -296,6 +301,22 @@
Log.v(TAG, "Broadcast: a=" + intent.getAction()
+ " user=" + intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1));
}
+ if (ACTION_REMOVE_GUEST.equals(intent.getAction())) {
+ int currentUser = ActivityManager.getCurrentUser();
+ UserInfo userInfo = mUserManager.getUserInfo(currentUser);
+ if (userInfo != null && userInfo.isGuest()) {
+ showExitGuestDialog(currentUser);
+ }
+ return;
+ }
+ if (Intent.ACTION_USER_ADDED.equals(intent.getAction())) {
+ final int currentId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
+ UserInfo userInfo = mUserManager.getUserInfo(currentId);
+ if (userInfo != null && userInfo.isGuest()) {
+ showGuestNotification(currentId);
+ }
+ }
+
if (Intent.ACTION_USER_SWITCHED.equals(intent.getAction())) {
if (mExitGuestDialog != null && mExitGuestDialog.isShowing()) {
mExitGuestDialog.cancel();
@@ -329,6 +350,24 @@
}
refreshUsers(forcePictureLoadForId);
}
+
+ private void showGuestNotification(int guestUserId) {
+ PendingIntent removeGuestPI = PendingIntent.getBroadcastAsUser(mContext,
+ 0, new Intent(ACTION_REMOVE_GUEST), 0, UserHandle.OWNER);
+ Notification notification = new Notification.Builder(mContext)
+ .setVisibility(Notification.VISIBILITY_SECRET)
+ .setPriority(Notification.PRIORITY_MIN)
+ .setSmallIcon(R.drawable.ic_person)
+ .setContentTitle(mContext.getString(R.string.guest_notification_title))
+ .setContentText(mContext.getString(R.string.guest_notification_text))
+ .setShowWhen(false)
+ .addAction(R.drawable.ic_delete,
+ mContext.getString(R.string.guest_notification_remove_action),
+ removeGuestPI)
+ .build();
+ NotificationManager.from(mContext).notifyAsUser(null, 0, notification,
+ new UserHandle(guestUserId));
+ }
};
private final ContentObserver mSettingsObserver = new ContentObserver(new Handler()) {
diff --git a/rs/java/android/renderscript/RenderScript.java b/rs/java/android/renderscript/RenderScript.java
index 5fc1f93..7c927fd 100644
--- a/rs/java/android/renderscript/RenderScript.java
+++ b/rs/java/android/renderscript/RenderScript.java
@@ -332,10 +332,12 @@
rsnClosureSetGlobal(mContext, closureID, fieldID, value, size);
}
- native long rsnScriptGroup2Create(long con, String cachePath, long[] closures);
- synchronized long nScriptGroup2Create(String cachePath, long[] closures) {
+ native long rsnScriptGroup2Create(long con, String name, String cachePath,
+ long[] closures);
+ synchronized long nScriptGroup2Create(String name, String cachePath,
+ long[] closures) {
validate();
- return rsnScriptGroup2Create(mContext, cachePath, closures);
+ return rsnScriptGroup2Create(mContext, name, cachePath, closures);
}
native void rsnScriptGroup2Execute(long con, long groupID);
diff --git a/rs/java/android/renderscript/ScriptGroup2.java b/rs/java/android/renderscript/ScriptGroup2.java
index 858a957..96bb6e2 100644
--- a/rs/java/android/renderscript/ScriptGroup2.java
+++ b/rs/java/android/renderscript/ScriptGroup2.java
@@ -323,6 +323,7 @@
}
}
+ String mName;
List<Closure> mClosures;
List<UnboundValue> mInputs;
Future[] mOutputs;
@@ -333,9 +334,10 @@
super(id, rs);
}
- ScriptGroup2(RenderScript rs, List<Closure> closures,
+ ScriptGroup2(RenderScript rs, String name, List<Closure> closures,
List<UnboundValue> inputs, Future[] outputs) {
super(0, rs);
+ mName = name;
mClosures = closures;
mInputs = inputs;
mOutputs = outputs;
@@ -344,7 +346,7 @@
for (int i = 0; i < closureIDs.length; i++) {
closureIDs[i] = closures.get(i).getID(rs);
}
- long id = rs.nScriptGroup2Create(ScriptC.mCachePath, closureIDs);
+ long id = rs.nScriptGroup2Create(name, ScriptC.mCachePath, closureIDs);
setID(id);
}
@@ -560,7 +562,7 @@
!name.equals(name.replaceAll("[^a-zA-Z0-9-]", "_"))) {
throw new RSIllegalArgumentException("invalid script group name");
}
- ScriptGroup2 ret = new ScriptGroup2(mRS, mClosures, mInputs, outputs);
+ ScriptGroup2 ret = new ScriptGroup2(mRS, name, mClosures, mInputs, outputs);
return ret;
}
diff --git a/rs/jni/android_renderscript_RenderScript.cpp b/rs/jni/android_renderscript_RenderScript.cpp
index 783df68..49afa6d 100644
--- a/rs/jni/android_renderscript_RenderScript.cpp
+++ b/rs/jni/android_renderscript_RenderScript.cpp
@@ -418,8 +418,9 @@
}
static long
-nScriptGroup2Create(JNIEnv *_env, jobject _this, jlong con,
+nScriptGroup2Create(JNIEnv *_env, jobject _this, jlong con, jstring name,
jstring cacheDir, jlongArray closureArray) {
+ AutoJavaStringToUTF8 nameUTF(_env, name);
AutoJavaStringToUTF8 cacheDirUTF(_env, cacheDir);
jlong* jClosures = _env->GetLongArrayElements(closureArray, nullptr);
@@ -430,7 +431,8 @@
}
return (jlong)(uintptr_t)rsScriptGroup2Create(
- (RsContext)con, cacheDirUTF.c_str(), cacheDirUTF.length(),
+ (RsContext)con, nameUTF.c_str(), nameUTF.length(),
+ cacheDirUTF.c_str(), cacheDirUTF.length(),
closures, numClosures);
}
@@ -2404,7 +2406,7 @@
{"rsnScriptInvokeIDCreate", "(JJI)J", (void*)nScriptInvokeIDCreate },
{"rsnScriptFieldIDCreate", "(JJI)J", (void*)nScriptFieldIDCreate },
{"rsnScriptGroupCreate", "(J[J[J[J[J[J)J", (void*)nScriptGroupCreate },
-{"rsnScriptGroup2Create", "(JLjava/lang/String;[J)J", (void*)nScriptGroup2Create },
+{"rsnScriptGroup2Create", "(JLjava/lang/String;Ljava/lang/String;[J)J", (void*)nScriptGroup2Create },
{"rsnScriptGroupSetInput", "(JJJJ)V", (void*)nScriptGroupSetInput },
{"rsnScriptGroupSetOutput", "(JJJJ)V", (void*)nScriptGroupSetOutput },
{"rsnScriptGroupExecute", "(JJ)V", (void*)nScriptGroupExecute },
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java
index 34e8b78..9f80fd8 100644
--- a/services/core/java/com/android/server/AlarmManagerService.java
+++ b/services/core/java/com/android/server/AlarmManagerService.java
@@ -99,6 +99,7 @@
static final boolean DEBUG_BATCH = localLOGV || false;
static final boolean DEBUG_VALIDATE = localLOGV || false;
static final boolean DEBUG_ALARM_CLOCK = localLOGV || false;
+ static final boolean RECORD_ALARMS_IN_HISTORY = true;
static final int ALARM_EVENT = 1;
static final String TIMEZONE_PROPERTY = "persist.sys.timezone";
@@ -1345,15 +1346,6 @@
}
void rescheduleKernelAlarmsLocked() {
- if (mPendingIdleUntil != null) {
- // If we have a pending "idle until" alarm, we will just blindly wait until
- // it is time for that alarm to go off. We don't want to wake up for any
- // other reasons.
- mNextWakeup = mNextNonWakeup = mPendingIdleUntil.whenElapsed;
- setLocked(ELAPSED_REALTIME_WAKEUP, mNextWakeup);
- setLocked(ELAPSED_REALTIME, mNextNonWakeup);
- return;
- }
// Schedule the next upcoming wakeup alarm. If there is a deliverable batch
// prior to that which contains no wakeups, we schedule that as well.
long nextNonWakeup = 0;
@@ -1574,13 +1566,6 @@
boolean triggerAlarmsLocked(ArrayList<Alarm> triggerList, final long nowELAPSED,
final long nowRTC) {
boolean hasWakeup = false;
- if (mPendingIdleUntil != null) {
- // If we have a pending "idle until" alarm, don't trigger any alarms
- // until we are past the idle period.
- if (nowELAPSED < mPendingIdleUntil.whenElapsed) {
- return false;
- }
- }
// batches are temporally sorted, so we need only pull from the
// start of the list until we either empty it or hit a batch
// that is not yet deliverable
@@ -1816,6 +1801,17 @@
if (localLOGV) {
Slog.v(TAG, "sending alarm " + alarm);
}
+ if (RECORD_ALARMS_IN_HISTORY) {
+ if (alarm.workSource != null && alarm.workSource.size() > 0) {
+ for (int wi=0; wi<alarm.workSource.size(); wi++) {
+ ActivityManagerNative.noteAlarmStart(
+ alarm.operation, alarm.workSource.get(wi), alarm.tag);
+ }
+ } else {
+ ActivityManagerNative.noteAlarmStart(
+ alarm.operation, -1, alarm.tag);
+ }
+ }
alarm.operation.send(getContext(), 0,
mBackgroundIntent.putExtra(
Intent.EXTRA_ALARM_COUNT, alarm.count),
@@ -1856,11 +1852,11 @@
for (int wi=0; wi<alarm.workSource.size(); wi++) {
ActivityManagerNative.noteWakeupAlarm(
alarm.operation, alarm.workSource.get(wi),
- alarm.workSource.getName(wi));
+ alarm.workSource.getName(wi), alarm.tag);
}
} else {
ActivityManagerNative.noteWakeupAlarm(
- alarm.operation, -1, null);
+ alarm.operation, -1, null, alarm.tag);
}
}
} catch (PendingIntent.CanceledException e) {
@@ -2222,6 +2218,17 @@
fs.nesting = 0;
fs.aggregateTime += nowELAPSED - fs.startTime;
}
+ if (RECORD_ALARMS_IN_HISTORY) {
+ if (inflight.mWorkSource != null && inflight.mWorkSource.size() > 0) {
+ for (int wi=0; wi<inflight.mWorkSource.size(); wi++) {
+ ActivityManagerNative.noteAlarmFinish(
+ pi, inflight.mWorkSource.get(wi), inflight.mTag);
+ }
+ } else {
+ ActivityManagerNative.noteAlarmFinish(
+ pi, -1, inflight.mTag);
+ }
+ }
} else {
mLog.w("No in-flight alarm for " + pi + " " + intent);
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 6341807..b606353 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -42,7 +42,6 @@
import android.app.ITaskStackListener;
import android.app.ProfilerInfo;
import android.app.usage.UsageEvents;
-import android.app.usage.UsageStats;
import android.app.usage.UsageStatsManagerInternal;
import android.appwidget.AppWidgetManager;
import android.content.res.Resources;
@@ -3449,6 +3448,35 @@
}
}
+ @Override
+ public int getPackageProcessState(String packageName) {
+ int procState = ActivityManager.PROCESS_STATE_NONEXISTENT;
+ synchronized (this) {
+ for (int i=mLruProcesses.size()-1; i>=0; i--) {
+ final ProcessRecord proc = mLruProcesses.get(i);
+ if (procState == ActivityManager.PROCESS_STATE_NONEXISTENT
+ || procState > proc.setProcState) {
+ boolean found = false;
+ for (int j=proc.pkgList.size()-1; j>=0 && !found; j--) {
+ if (proc.pkgList.keyAt(j).equals(packageName)) {
+ procState = proc.setProcState;
+ found = true;
+ }
+ }
+ if (proc.pkgDeps != null && !found) {
+ for (int j=proc.pkgDeps.size()-1; j>=0; j--) {
+ if (proc.pkgDeps.valueAt(j).equals(packageName)) {
+ procState = proc.setProcState;
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ return procState;
+ }
+
private void dispatchProcessesChanged() {
int N;
synchronized (this) {
@@ -10501,15 +10529,11 @@
Context.WINDOW_SERVICE)).addView(v, lp);
}
- public void noteWakeupAlarm(IIntentSender sender, int sourceUid, String sourcePkg) {
+ public void noteWakeupAlarm(IIntentSender sender, int sourceUid, String sourcePkg, String tag) {
if (!(sender instanceof PendingIntentRecord)) {
return;
}
final PendingIntentRecord rec = (PendingIntentRecord)sender;
- final String tag;
- synchronized (this) {
- tag = getTagForIntentSenderLocked(rec, "*walarm*:");
- }
final BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics();
synchronized (stats) {
if (mBatteryStatsService.isOnBattery()) {
@@ -10524,6 +10548,34 @@
}
}
+ public void noteAlarmStart(IIntentSender sender, int sourceUid, String tag) {
+ if (!(sender instanceof PendingIntentRecord)) {
+ return;
+ }
+ final PendingIntentRecord rec = (PendingIntentRecord)sender;
+ final BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics();
+ synchronized (stats) {
+ mBatteryStatsService.enforceCallingPermission();
+ int MY_UID = Binder.getCallingUid();
+ int uid = rec.uid == MY_UID ? Process.SYSTEM_UID : rec.uid;
+ mBatteryStatsService.noteAlarmStart(tag, sourceUid >= 0 ? sourceUid : uid);
+ }
+ }
+
+ public void noteAlarmFinish(IIntentSender sender, int sourceUid, String tag) {
+ if (!(sender instanceof PendingIntentRecord)) {
+ return;
+ }
+ final PendingIntentRecord rec = (PendingIntentRecord)sender;
+ final BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics();
+ synchronized (stats) {
+ mBatteryStatsService.enforceCallingPermission();
+ int MY_UID = Binder.getCallingUid();
+ int uid = rec.uid == MY_UID ? Process.SYSTEM_UID : rec.uid;
+ mBatteryStatsService.noteAlarmFinish(tag, sourceUid >= 0 ? sourceUid : uid);
+ }
+ }
+
public boolean killPids(int[] pids, String pReason, boolean secure) {
if (Binder.getCallingUid() != Process.SYSTEM_UID) {
throw new SecurityException("killPids only available to the system");
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index ed108c2..bfc4fc7 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -315,6 +315,20 @@
}
}
+ public void noteAlarmStart(String name, int uid) {
+ enforceCallingPermission();
+ synchronized (mStats) {
+ mStats.noteAlarmStartLocked(name, uid);
+ }
+ }
+
+ public void noteAlarmFinish(String name, int uid) {
+ enforceCallingPermission();
+ synchronized (mStats) {
+ mStats.noteAlarmFinishLocked(name, uid);
+ }
+ }
+
public void noteStartWakelock(int uid, int pid, String name, String historyName, int type,
boolean unimportantForLogging) {
enforceCallingPermission();
@@ -884,7 +898,7 @@
pw.println(" Enable or disable a running option. Option state is not saved across boots.");
pw.println(" Options are:");
pw.println(" full-history: include additional detailed events in battery history:");
- pw.println(" wake_lock_in and proc events");
+ pw.println(" wake_lock_in, alarms and proc events");
pw.println(" no-auto-reset: don't automatically reset stats when unplugged");
}
diff --git a/services/core/java/com/android/server/job/controllers/AppIdleController.java b/services/core/java/com/android/server/job/controllers/AppIdleController.java
index 8a9f3e1..23d5c05 100644
--- a/services/core/java/com/android/server/job/controllers/AppIdleController.java
+++ b/services/core/java/com/android/server/job/controllers/AppIdleController.java
@@ -49,16 +49,12 @@
private static volatile AppIdleController sController;
final ArrayList<JobStatus> mTrackedTasks = new ArrayList<JobStatus>();
private final UsageStatsManagerInternal mUsageStatsInternal;
- private final BatteryManagerInternal mBatteryManagerInternal;
+ private final BatteryManager mBatteryManager;
private boolean mPluggedIn;
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override public void onReceive(Context context, Intent intent) {
- if (Intent.ACTION_BATTERY_CHANGED.equals(intent.getAction())) {
- int plugged = intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0);
- // TODO: Allow any charger type
- onPluggedIn((plugged & BatteryManager.BATTERY_PLUGGED_AC) != 0);
- }
+ onPluggedIn(mBatteryManager.isCharging());
}
};
@@ -74,23 +70,19 @@
private AppIdleController(StateChangedListener stateChangedListener, Context context) {
super(stateChangedListener, context);
mUsageStatsInternal = LocalServices.getService(UsageStatsManagerInternal.class);
- mBatteryManagerInternal = LocalServices.getService(BatteryManagerInternal.class);
- mPluggedIn = isPowered();
+ mBatteryManager = context.getSystemService(BatteryManager.class);
+ mPluggedIn = mBatteryManager.isCharging();
mUsageStatsInternal.addAppIdleStateChangeListener(this);
registerReceivers();
}
private void registerReceivers() {
// Monitor battery charging state
- IntentFilter filter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
+ IntentFilter filter = new IntentFilter(BatteryManager.ACTION_CHARGING);
+ filter.addAction(BatteryManager.ACTION_DISCHARGING);
mContext.registerReceiver(mReceiver, filter);
}
- private boolean isPowered() {
- // TODO: Allow any charger type
- return mBatteryManagerInternal.isPowered(BatteryManager.BATTERY_PLUGGED_AC);
- }
-
@Override
public void maybeStartTrackingJob(JobStatus jobStatus) {
synchronized (mTrackedTasks) {