summaryrefslogtreecommitdiffstats
path: root/core/java/android
diff options
context:
space:
mode:
Diffstat (limited to 'core/java/android')
-rw-r--r--core/java/android/app/ActivityManager.java93
-rw-r--r--core/java/android/app/ActivityManagerNative.java115
-rw-r--r--core/java/android/app/ApplicationPackageManager.java111
-rw-r--r--core/java/android/app/IActivityManager.java11
-rw-r--r--core/java/android/app/admin/DevicePolicyManager.java16
-rw-r--r--core/java/android/app/admin/IDevicePolicyManager.aidl1
-rw-r--r--core/java/android/content/ComponentName.java51
-rw-r--r--core/java/android/content/pm/PackageParser.java18
-rw-r--r--core/java/android/hardware/Camera.java8
-rw-r--r--core/java/android/hardware/camera2/CameraCaptureSession.java201
-rw-r--r--core/java/android/hardware/camera2/CameraDevice.java102
-rw-r--r--core/java/android/hardware/camera2/CameraManager.java4
-rw-r--r--core/java/android/hardware/camera2/CaptureRequest.java35
-rw-r--r--core/java/android/hardware/camera2/CaptureResult.java13
-rw-r--r--core/java/android/hardware/camera2/ICameraDeviceCallbacks.aidl1
-rw-r--r--core/java/android/hardware/camera2/ICameraDeviceUser.aidl31
-rw-r--r--core/java/android/hardware/camera2/impl/CallbackProxies.java7
-rw-r--r--core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java58
-rw-r--r--core/java/android/hardware/camera2/impl/CameraDeviceImpl.java470
-rw-r--r--core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java30
-rw-r--r--core/java/android/hardware/camera2/params/InputConfiguration.java115
-rw-r--r--core/java/android/hardware/fingerprint/FingerprintManager.java66
-rw-r--r--core/java/android/hardware/fingerprint/IFingerprintService.aidl2
-rw-r--r--core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl2
-rw-r--r--core/java/android/hardware/location/GeofenceHardwareImpl.java18
-rw-r--r--core/java/android/hardware/location/IFusedLocationHardware.aidl5
-rw-r--r--core/java/android/net/ConnectivityManager.java31
-rw-r--r--core/java/android/net/IConnectivityManager.aidl2
-rw-r--r--core/java/android/net/Network.java29
-rw-r--r--core/java/android/net/NetworkAgent.java39
-rw-r--r--core/java/android/net/NetworkMisc.java10
-rw-r--r--core/java/android/nfc/cardemulation/ApduServiceInfo.java15
-rw-r--r--core/java/android/os/BatteryStats.java10
-rw-r--r--core/java/android/os/Binder.java7
-rw-r--r--core/java/android/os/Debug.java90
-rw-r--r--core/java/android/os/storage/DiskInfo.java27
-rw-r--r--core/java/android/os/storage/IMountService.java64
-rw-r--r--core/java/android/os/storage/IMountServiceListener.java26
-rw-r--r--core/java/android/os/storage/StorageEventListener.java3
-rw-r--r--core/java/android/os/storage/StorageManager.java81
-rw-r--r--core/java/android/os/storage/VolumeInfo.java80
-rw-r--r--core/java/android/provider/Settings.java11
-rw-r--r--core/java/android/service/gatekeeper/IGateKeeperService.aidl65
-rw-r--r--core/java/android/service/voice/VoiceInteractionSession.java17
-rw-r--r--core/java/android/service/voice/VoiceInteractionSessionService.java25
-rw-r--r--core/java/android/text/StaticLayout.java2
-rw-r--r--core/java/android/view/PhoneWindow.java8
-rw-r--r--core/java/android/view/ThreadedRenderer.java4
-rw-r--r--core/java/android/view/View.java206
-rw-r--r--core/java/android/view/Window.java2
-rw-r--r--core/java/android/view/WindowManager.java41
-rw-r--r--core/java/android/widget/Editor.java22
-rw-r--r--core/java/android/widget/PopupWindow.java13
-rw-r--r--core/java/android/widget/TextView.java4
54 files changed, 2075 insertions, 443 deletions
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 class ActivityManager {
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 class ActivityManager {
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 @@ public class ActivityManager {
}
/**
+ * 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 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM
}
}
- 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 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM
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 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM
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 @@ class ActivityManagerProxy implements IActivityManager
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 @@ class ActivityManagerProxy implements IActivityManager
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 @@ class ActivityManagerProxy implements IActivityManager
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 @@ class ActivityManagerProxy implements IActivityManager
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 @@ class ActivityManagerProxy implements IActivityManager
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 @@ class ActivityManagerProxy implements IActivityManager
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 @@ class ActivityManagerProxy implements IActivityManager
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 @@ class ActivityManagerProxy implements IActivityManager
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 @@ class ActivityManagerProxy implements IActivityManager
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 @@ class ActivityManagerProxy implements IActivityManager
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 @@ class ActivityManagerProxy implements IActivityManager
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,10 +5768,24 @@ class ActivityManagerProxy implements IActivityManager
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/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 9ddfd88..fe6e4f3 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -17,6 +17,8 @@
package android.app;
import android.annotation.DrawableRes;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.StringRes;
import android.annotation.XmlRes;
import android.content.ComponentName;
@@ -733,13 +735,16 @@ final class ApplicationPackageManager extends PackageManager {
}
}
- @Override public Drawable getDrawable(String packageName, @DrawableRes int resid,
- ApplicationInfo appInfo) {
- ResourceName name = new ResourceName(packageName, resid);
- Drawable dr = getCachedIcon(name);
- if (dr != null) {
- return dr;
+ @Nullable
+ @Override
+ public Drawable getDrawable(String packageName, @DrawableRes int resId,
+ @Nullable ApplicationInfo appInfo) {
+ final ResourceName name = new ResourceName(packageName, resId);
+ final Drawable cachedIcon = getCachedIcon(name);
+ if (cachedIcon != null) {
+ return cachedIcon;
}
+
if (appInfo == null) {
try {
appInfo = getApplicationInfo(packageName, sDefaultFlags);
@@ -747,36 +752,46 @@ final class ApplicationPackageManager extends PackageManager {
return null;
}
}
- try {
- Resources r = getResourcesForApplication(appInfo);
- dr = r.getDrawable(resid);
- if (false) {
- RuntimeException e = new RuntimeException("here");
- e.fillInStackTrace();
- Log.w(TAG, "Getting drawable 0x" + Integer.toHexString(resid)
- + " from package " + packageName
- + ": app scale=" + r.getCompatibilityInfo().applicationScale
- + ", caller scale=" + mContext.getResources().getCompatibilityInfo().applicationScale,
- e);
+
+ if (resId != 0) {
+ try {
+ final Resources r = getResourcesForApplication(appInfo);
+ final Drawable dr = r.getDrawable(resId, null);
+ if (dr != null) {
+ putCachedIcon(name, dr);
+ }
+
+ if (false) {
+ RuntimeException e = new RuntimeException("here");
+ e.fillInStackTrace();
+ Log.w(TAG, "Getting drawable 0x" + Integer.toHexString(resId)
+ + " from package " + packageName
+ + ": app scale=" + r.getCompatibilityInfo().applicationScale
+ + ", caller scale=" + mContext.getResources()
+ .getCompatibilityInfo().applicationScale,
+ e);
+ }
+ if (DEBUG_ICONS) {
+ Log.v(TAG, "Getting drawable 0x"
+ + Integer.toHexString(resId) + " from " + r
+ + ": " + dr);
+ }
+ return dr;
+ } catch (NameNotFoundException e) {
+ Log.w("PackageManager", "Failure retrieving resources for "
+ + appInfo.packageName);
+ } catch (Resources.NotFoundException e) {
+ Log.w("PackageManager", "Failure retrieving resources for "
+ + appInfo.packageName + ": " + e.getMessage());
+ } catch (Exception e) {
+ // If an exception was thrown, fall through to return
+ // default icon.
+ Log.w("PackageManager", "Failure retrieving icon 0x"
+ + Integer.toHexString(resId) + " in package "
+ + packageName, e);
}
- if (DEBUG_ICONS) Log.v(TAG, "Getting drawable 0x"
- + Integer.toHexString(resid) + " from " + r
- + ": " + dr);
- putCachedIcon(name, dr);
- return dr;
- } catch (NameNotFoundException e) {
- Log.w("PackageManager", "Failure retrieving resources for "
- + appInfo.packageName);
- } catch (Resources.NotFoundException e) {
- Log.w("PackageManager", "Failure retrieving resources for "
- + appInfo.packageName + ": " + e.getMessage());
- } catch (RuntimeException e) {
- // If an exception was thrown, fall through to return
- // default icon.
- Log.w("PackageManager", "Failure retrieving icon 0x"
- + Integer.toHexString(resid) + " in package "
- + packageName, e);
}
+
return null;
}
@@ -923,19 +938,21 @@ final class ApplicationPackageManager extends PackageManager {
return label;
}
- @Override public Resources getResourcesForActivity(
- ComponentName activityName) throws NameNotFoundException {
+ @Override
+ public Resources getResourcesForActivity(ComponentName activityName)
+ throws NameNotFoundException {
return getResourcesForApplication(
getActivityInfo(activityName, sDefaultFlags).applicationInfo);
}
- @Override public Resources getResourcesForApplication(
- ApplicationInfo app) throws NameNotFoundException {
+ @Override
+ public Resources getResourcesForApplication(@NonNull ApplicationInfo app)
+ throws NameNotFoundException {
if (app.packageName.equals("system")) {
return mContext.mMainThread.getSystemContext().getResources();
}
final boolean sameUid = (app.uid == Process.myUid());
- Resources r = mContext.mMainThread.getTopLevelResources(
+ final Resources r = mContext.mMainThread.getTopLevelResources(
sameUid ? app.sourceDir : app.publicSourceDir,
sameUid ? app.splitSourceDirs : app.splitPublicSourceDirs,
app.resourceDirs, app.sharedLibraryFiles, Display.DEFAULT_DISPLAY,
@@ -946,8 +963,9 @@ final class ApplicationPackageManager extends PackageManager {
throw new NameNotFoundException("Unable to open " + app.publicSourceDir);
}
- @Override public Resources getResourcesForApplication(
- String appPackageName) throws NameNotFoundException {
+ @Override
+ public Resources getResourcesForApplication(String appPackageName)
+ throws NameNotFoundException {
return getResourcesForApplication(
getApplicationInfo(appPackageName, sDefaultFlags));
}
@@ -999,13 +1017,14 @@ final class ApplicationPackageManager extends PackageManager {
mPM = pm;
}
- private Drawable getCachedIcon(ResourceName name) {
+ @Nullable
+ private Drawable getCachedIcon(@NonNull ResourceName name) {
synchronized (sSync) {
- WeakReference<Drawable.ConstantState> wr = sIconCache.get(name);
+ final WeakReference<Drawable.ConstantState> wr = sIconCache.get(name);
if (DEBUG_ICONS) Log.v(TAG, "Get cached weak drawable ref for "
+ name + ": " + wr);
if (wr != null) { // we have the activity
- Drawable.ConstantState state = wr.get();
+ final Drawable.ConstantState state = wr.get();
if (state != null) {
if (DEBUG_ICONS) {
Log.v(TAG, "Get cached drawable state for " + name + ": " + state);
@@ -1025,9 +1044,9 @@ final class ApplicationPackageManager extends PackageManager {
return null;
}
- private void putCachedIcon(ResourceName name, Drawable dr) {
+ private void putCachedIcon(@NonNull ResourceName name, @NonNull Drawable dr) {
synchronized (sSync) {
- sIconCache.put(name, new WeakReference<Drawable.ConstantState>(dr.getConstantState()));
+ sIconCache.put(name, new WeakReference<>(dr.getConstantState()));
if (DEBUG_ICONS) Log.v(TAG, "Added cached drawable state for " + name + ": " + dr);
}
}
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 interface IActivityManager extends IInterface {
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 @@ public interface IActivityManager extends IInterface {
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 @@ public interface IActivityManager extends IInterface {
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/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index a0a6c4c..44760ce 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -4210,4 +4210,20 @@ public class DevicePolicyManager {
return false;
}
}
+
+ /**
+ * Called by device owner to set the enabled state of the status bar. Disabling the status
+ * bar blocks notifications, quick settings and other screen overlays that allow escaping from
+ * a single use device.
+ *
+ * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+ * @param enabled New state of the status bar.
+ */
+ public void setStatusBarEnabledState(ComponentName admin, boolean enabled) {
+ try {
+ mService.setStatusBarEnabledState(admin, enabled);
+ } catch (RemoteException re) {
+ Log.w(TAG, "Failed talking with device policy service", re);
+ }
+ }
}
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 131b99c..7502e1d 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -223,4 +223,5 @@ interface IDevicePolicyManager {
PersistableBundle getOtaPolicy();
boolean setKeyguardEnabledState(in ComponentName admin, boolean enabled);
+ void setStatusBarEnabledState(in ComponentName who, boolean enabled);
}
diff --git a/core/java/android/content/ComponentName.java b/core/java/android/content/ComponentName.java
index 547a2c3..8aeb22d 100644
--- a/core/java/android/content/ComponentName.java
+++ b/core/java/android/content/ComponentName.java
@@ -18,6 +18,7 @@ package android.content;
import android.os.Parcel;
import android.os.Parcelable;
+import android.text.TextUtils;
import java.io.PrintWriter;
import java.lang.Comparable;
@@ -37,6 +38,56 @@ public final class ComponentName implements Parcelable, Cloneable, Comparable<Co
private final String mClass;
/**
+ * Create a new component identifier where the class name may be specified
+ * as either absolute or relative to the containing package.
+ *
+ * <p>Relative package names begin with a <code>'.'</code> character. For a package
+ * <code>"com.example"</code> and class name <code>".app.MyActivity"</code> this method
+ * will return a ComponentName with the package <code>"com.example"</code>and class name
+ * <code>"com.example.app.MyActivity"</code>. Fully qualified class names are also
+ * permitted.</p>
+ *
+ * @param pkg the name of the package the component exists in
+ * @param cls the name of the class inside of <var>pkg</var> that implements
+ * the component
+ * @return the new ComponentName
+ */
+ public static ComponentName createRelative(String pkg, String cls) {
+ if (TextUtils.isEmpty(cls)) {
+ throw new IllegalArgumentException("class name cannot be empty");
+ }
+
+ final String fullName;
+ if (cls.charAt(0) == '.') {
+ // Relative to the package. Prepend the package name.
+ fullName = pkg + cls;
+ } else {
+ // Fully qualified package name.
+ fullName = cls;
+ }
+ return new ComponentName(pkg, fullName);
+ }
+
+ /**
+ * Create a new component identifier where the class name may be specified
+ * as either absolute or relative to the containing package.
+ *
+ * <p>Relative package names begin with a <code>'.'</code> character. For a package
+ * <code>"com.example"</code> and class name <code>".app.MyActivity"</code> this method
+ * will return a ComponentName with the package <code>"com.example"</code>and class name
+ * <code>"com.example.app.MyActivity"</code>. Fully qualified class names are also
+ * permitted.</p>
+ *
+ * @param pkg a Context for the package implementing the component
+ * @param cls the name of the class inside of <var>pkg</var> that implements
+ * the component
+ * @return the new ComponentName
+ */
+ public static ComponentName createRelative(Context pkg, String cls) {
+ return createRelative(pkg.getPackageName(), cls);
+ }
+
+ /**
* Create a new component identifier.
*
* @param pkg The name of the package that the component exists in. Can
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 763a017..7464cab 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -4655,7 +4655,7 @@ public class PackageParser {
private static boolean copyNeeded(int flags, Package p,
PackageUserState state, Bundle metaData, int userId) {
- if (userId != 0) {
+ if (userId != UserHandle.USER_OWNER) {
// We always need to copy for other users, since we need
// to fix up the uid.
return true;
@@ -4737,11 +4737,9 @@ public class PackageParser {
// Make shallow copy so we can store the metadata/libraries safely
ApplicationInfo ai = new ApplicationInfo(p.applicationInfo);
- if (userId != 0) {
- ai.uid = UserHandle.getUid(userId, ai.uid);
- ai.dataDir = PackageManager.getDataDirForUser(ai.volumeUuid, ai.packageName, userId)
- .getAbsolutePath();
- }
+ ai.uid = UserHandle.getUid(userId, ai.uid);
+ ai.dataDir = PackageManager.getDataDirForUser(ai.volumeUuid, ai.packageName, userId)
+ .getAbsolutePath();
if ((flags & PackageManager.GET_META_DATA) != 0) {
ai.metaData = p.mAppMetaData;
}
@@ -4766,11 +4764,9 @@ public class PackageParser {
// This is only used to return the ResolverActivity; we will just always
// make a copy.
ai = new ApplicationInfo(ai);
- if (userId != 0) {
- ai.uid = UserHandle.getUid(userId, ai.uid);
- ai.dataDir = PackageManager.getDataDirForUser(ai.volumeUuid, ai.packageName, userId)
- .getAbsolutePath();
- }
+ ai.uid = UserHandle.getUid(userId, ai.uid);
+ ai.dataDir = PackageManager.getDataDirForUser(ai.volumeUuid, ai.packageName, userId)
+ .getAbsolutePath();
if (state.stopped) {
ai.flags |= ApplicationInfo.FLAG_STOPPED;
} else {
diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java
index 49f6513..d88594d 100644
--- a/core/java/android/hardware/Camera.java
+++ b/core/java/android/hardware/Camera.java
@@ -1803,8 +1803,6 @@ public class Camera {
public Point mouth = null;
}
- // Error codes match the enum in include/ui/Camera.h
-
/**
* Unspecified camera error.
* @see Camera.ErrorCallback
@@ -1812,6 +1810,12 @@ public class Camera {
public static final int CAMERA_ERROR_UNKNOWN = 1;
/**
+ * Camera was disconnected due to use by higher priority user.
+ * @see Camera.ErrorCallback
+ */
+ public static final int CAMERA_ERROR_EVICTED = 2;
+
+ /**
* Media server died. In this case, the application must release the
* Camera object and instantiate a new one.
* @see Camera.ErrorCallback
diff --git a/core/java/android/hardware/camera2/CameraCaptureSession.java b/core/java/android/hardware/camera2/CameraCaptureSession.java
index ce83028..31e6e25 100644
--- a/core/java/android/hardware/camera2/CameraCaptureSession.java
+++ b/core/java/android/hardware/camera2/CameraCaptureSession.java
@@ -17,21 +17,32 @@
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
@@ -59,6 +70,61 @@ public abstract class CameraCaptureSession implements AutoCloseable {
public abstract CameraDevice getDevice();
/**
+ * <p>Pre-allocate all buffers for an output Surface.</p>
+ *
+ * <p>Normally, the image buffers for a given output Surface are allocated on-demand,
+ * to minimize startup latency and memory overhead.</p>
+ *
+ * <p>However, in some cases, it may be desirable for the buffers to be allocated before
+ * any requests targeting the Surface are actually submitted to the device. Large buffers
+ * may take some time to allocate, which can result in delays in submitting requests until
+ * sufficient buffers are allocated to reach steady-state behavior. Such delays can cause
+ * bursts to take longer than desired, or cause skips or stutters in preview output.</p>
+ *
+ * <p>The prepare() method can be used to perform this preallocation. It may only be called for
+ * a given output Surface before that Surface is used as a target for a request. The number of
+ * buffers allocated is the sum of the count needed by the consumer providing the output
+ * Surface, and the maximum number needed by the camera device to fill its pipeline. Since this
+ * may be a larger number than what is actually required for steady-state operation, using
+ * prepare may result in higher memory consumption than the normal on-demand behavior results
+ * in. Prepare() will also delay the time to first output to a given Surface, in exchange for
+ * smoother frame rate once the allocation is complete.</p>
+ *
+ * <p>For example, an application that creates an
+ * {@link android.media.ImageReader#newInstance ImageReader} with a maxImages argument of 10,
+ * but only uses 3 simultaneous Images at once would normally only cause those 3 images to be
+ * allocated (plus what is needed by the camera device for smooth operation). But using
+ * prepare() on the ImageReader Surface will result in all 10 Images being allocated. So
+ * applications using this method should take care to request only the number of buffers
+ * actually necessary for their application.</p>
+ *
+ * <p>If the same output Surface is used in consecutive sessions (without closing the first
+ * session explicitly), then its already-allocated buffers are carried over, and if it was
+ * used as a target of a capture request in the first session, prepare cannot be called on it
+ * in the second session.</p>
+ *
+ * <p>Once allocation is complete, {@link StateCallback#onSurfacePrepared} will be invoked with
+ * the Surface provided to this method. Between the prepare call and the onSurfacePrepared call,
+ * the Surface provided to prepare must not be used as a target of a CaptureRequest submitted
+ * to this session.</p>
+ *
+ * @param surface the output Surface for which buffers should be pre-allocated. Must be one of
+ * the output Surfaces used to create this session.
+ *
+ * @throws CameraAccessException if the camera device is no longer connected or has
+ * encountered a fatal error
+ * @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 Surface is invalid, not part of this Session, or has
+ * already been used as a target of a CaptureRequest in this
+ * session or immediately prior sessions.
+ *
+ * @see StateCallback#onSurfacePrepared
+ */
+ public abstract void prepare(Surface surface) throws CameraAccessException;
+
+ /**
* <p>Submit a request for an image to be captured by the camera device.</p>
*
* <p>The request defines all the parameters for capturing the single image,
@@ -77,6 +143,12 @@ public abstract class CameraCaptureSession implements AutoCloseable {
* {@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 +166,10 @@ public abstract class CameraCaptureSession implements AutoCloseable {
* 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 a reprocess
+ * capture request is submitted in a non-reprocessible capture
+ * session; or the capture targets a Surface in the middle
+ * of being {@link #prepare prepared}; or the handler is
* null, the listener is not null, and the calling thread has
* no looper.
*
@@ -102,6 +177,7 @@ public abstract class CameraCaptureSession implements AutoCloseable {
* @see #setRepeatingRequest
* @see #setRepeatingBurst
* @see #abortCaptures
+ * @see CameraDevice#createReprocessibleCaptureSession
*/
public abstract int capture(CaptureRequest request, CaptureCallback listener, Handler handler)
throws CameraAccessException;
@@ -121,6 +197,13 @@ public abstract class CameraCaptureSession implements AutoCloseable {
* {@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 +221,15 @@ public abstract class CameraCaptureSession implements AutoCloseable {
* @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 the requests target
+ * Surfaces not currently configured as outputs; or a reprocess
+ * capture request is submitted in a non-reprocessible capture
+ * session; or the list of requests contains both requests to
+ * capture images from the camera and reprocess capture
+ * requests; or one of the captures targets a Surface in the
+ * middle of being {@link #prepare prepared}; or if the handler
+ * is null, the listener is not null, and the calling thread
+ * has no looper.
*
* @see #capture
* @see #setRepeatingRequest
@@ -175,6 +264,14 @@ public abstract class CameraCaptureSession implements AutoCloseable {
* 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
@@ -192,10 +289,12 @@ public abstract class CameraCaptureSession implements AutoCloseable {
* @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 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.
+ * @throws IllegalArgumentException If the request references no Surfaces or references Surfaces
+ * that are not currently configured as outputs; or the request
+ * is a reprocess capture request; or the capture targets a
+ * Surface in the middle of being {@link #prepare prepared}; or
+ * the handler is null, the listener is not null, and the
+ * calling thread has no looper; or no requests were passed in.
*
* @see #capture
* @see #captureBurst
@@ -235,6 +334,14 @@ public abstract class CameraCaptureSession implements AutoCloseable {
* 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
@@ -252,10 +359,13 @@ public abstract class CameraCaptureSession implements AutoCloseable {
* @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 reference 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. Or if no requests were passed in.
+ * @throws IllegalArgumentException If the requests reference no Surfaces or reference Surfaces
+ * not currently configured as outputs; or one of the requests
+ * is a reprocess capture request; or one of the captures
+ * targets a Surface in the middle of being
+ * {@link #prepare prepared}; or the handler is null, the
+ * listener is not null, and the calling thread has no looper;
+ * or no requests were passed in.
*
* @see #capture
* @see #captureBurst
@@ -298,9 +408,10 @@ public abstract class CameraCaptureSession implements AutoCloseable {
* 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 +432,39 @@ public abstract class CameraCaptureSession implements AutoCloseable {
* @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
@@ -436,6 +576,25 @@ public abstract class CameraCaptureSession implements AutoCloseable {
public void onClosed(CameraCaptureSession session) {
// default empty implementation
}
+
+ /**
+ * This method is called when the buffer pre-allocation for an output Surface is complete.
+ *
+ * <p>Buffer pre-allocation for an output Surface is started by the {@link #prepare} call.
+ * While allocation is underway, the Surface must not be used as a capture target.
+ * Once this callback fires, the output Surface provided can again be used as a target for
+ * a capture request.</p>
+ *
+ * <p>In case of a error during pre-allocation (such as running out of suitable memory),
+ * this callback is still invoked after the error is encountered, though some buffers may
+ * not have been successfully pre-allocated.</p>
+ *
+ * @param session the session returned by {@link CameraDevice#createCaptureSession}
+ * @param surface the Surface that was used with the {@link #prepare} call.
+ */
+ public void onSurfacePrepared(CameraCaptureSession session, Surface surface) {
+ // default empty implementation
+ }
}
/**
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 @@ public abstract class CameraDevice implements AutoCloseable {
*
* <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 @@ public abstract class CameraDevice implements AutoCloseable {
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 @@ public abstract class CameraDevice implements AutoCloseable {
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 @@ public final class CameraManager {
* 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 @@ public final class CameraManager {
* 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 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
private final HashSet<Surface> mSurfaceSet;
private final CameraMetadataNative mSettings;
+ private boolean mIsReprocess;
private Object mUserTag;
@@ -168,6 +169,7 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
private CaptureRequest() {
mSettings = new CameraMetadataNative();
mSurfaceSet = new HashSet<Surface>();
+ mIsReprocess = false;
}
/**
@@ -179,6 +181,7 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
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 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
*
* 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 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
}
/**
+ * 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 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
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 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
Surface s = (Surface) p;
mSurfaceSet.add(s);
}
+
+ mIsReprocess = (in.readInt() == 0) ? false : true;
}
@Override
@@ -334,6 +358,7 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
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 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
*
* @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 @@ public class CaptureResult extends CameraMetadata<CaptureResult.Key<?>> {
* <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/ICameraDeviceCallbacks.aidl b/core/java/android/hardware/camera2/ICameraDeviceCallbacks.aidl
index ca0935c..151c918 100644
--- a/core/java/android/hardware/camera2/ICameraDeviceCallbacks.aidl
+++ b/core/java/android/hardware/camera2/ICameraDeviceCallbacks.aidl
@@ -31,4 +31,5 @@ interface ICameraDeviceCallbacks
oneway void onCaptureStarted(in CaptureResultExtras resultExtras, long timestamp);
oneway void onResultReceived(in CameraMetadataNative result,
in CaptureResultExtras resultExtras);
+ oneway void onPrepared(int streamId);
}
diff --git a/core/java/android/hardware/camera2/ICameraDeviceUser.aidl b/core/java/android/hardware/camera2/ICameraDeviceUser.aidl
index 01f2396..375b310 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 @@ interface ICameraDeviceUser
// 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);
@@ -75,4 +98,6 @@ interface ICameraDeviceUser
int waitUntilIdle();
int flush(out LongParcelable lastFrameNumber);
+
+ int prepare(int streamId);
}
diff --git a/core/java/android/hardware/camera2/impl/CallbackProxies.java b/core/java/android/hardware/camera2/impl/CallbackProxies.java
index f0217ac..dac2ef8 100644
--- a/core/java/android/hardware/camera2/impl/CallbackProxies.java
+++ b/core/java/android/hardware/camera2/impl/CallbackProxies.java
@@ -23,6 +23,7 @@ import android.hardware.camera2.CaptureResult;
import android.hardware.camera2.TotalCaptureResult;
import android.hardware.camera2.dispatch.Dispatchable;
import android.hardware.camera2.dispatch.MethodNameInvoker;
+import android.view.Surface;
import static com.android.internal.util.Preconditions.*;
@@ -175,6 +176,12 @@ public class CallbackProxies {
public void onClosed(CameraCaptureSession session) {
mProxy.invoke("onClosed", session);
}
+
+ @Override
+ public void onSurfacePrepared(CameraCaptureSession session, Surface surface) {
+ mProxy.invoke("onSurfacePrepared", session, surface);
+ }
+
}
private CallbackProxies() {
diff --git a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
index e87a2f8..c74204d 100644
--- a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
@@ -44,6 +44,8 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession {
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 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession {
* 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 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession {
// TODO: extra verification of outputs
mOutputs = outputs;
+ mInput = input;
mStateHandler = checkHandler(stateHandler);
mStateCallback = createUserStateCallbackProxy(mStateHandler, callback);
@@ -141,12 +144,21 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession {
}
@Override
+ public void prepare(Surface surface) throws CameraAccessException {
+ mDeviceImpl.prepare(surface);
+ }
+
+ @Override
public synchronized int capture(CaptureRequest request, CaptureCallback callback,
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 +181,19 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession {
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 +213,11 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession {
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 +240,13 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession {
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 +292,16 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession {
// 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.
*
@@ -549,6 +594,13 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession {
}
}
}
+
+ @Override
+ public void onSurfacePrepared(Surface surface) {
+ if (VERBOSE) Log.v(TAG, mIdString + "onPrepared");
+ mStateCallback.onSurfacePrepared(session, surface);
+ }
+
};
}
@@ -658,8 +710,8 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession {
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..1e680dfd 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.CaptureFailure;
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.IBinder;
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.HashMap;
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 @@ public class CameraDeviceImpl extends CameraDevice {
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 @@ public class CameraDeviceImpl extends CameraDevice {
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 @@ public class CameraDeviceImpl extends CameraDevice {
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 @@ public class CameraDeviceImpl extends CameraDevice {
for (Surface surface : outputs) {
outConfigurations.add(new OutputConfiguration(surface));
}
- createCaptureSessionByOutputConfiguration(outConfigurations, callback, handler);
+ createCaptureSessionInternal(null, outConfigurations, callback, handler);
}
@Override
@@ -437,9 +472,39 @@ public class CameraDeviceImpl extends CameraDevice {
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 @@ public class CameraDeviceImpl extends CameraDevice {
// 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 @@ public class CameraDeviceImpl extends CameraDevice {
}
// 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,48 @@ public class CameraDeviceImpl extends CameraDevice {
}
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 void prepare(Surface surface) throws CameraAccessException {
+ synchronized(mInterfaceLock) {
+ int streamId = -1;
+ for (int i = 0; i < mConfiguredOutputs.size(); i++) {
+ if (surface == mConfiguredOutputs.valueAt(i).getSurface()) {
+ streamId = mConfiguredOutputs.keyAt(i);
+ break;
+ }
+ }
+ if (streamId == -1) {
+ throw new IllegalArgumentException("Surface is not part of this session");
+ }
+ try {
+ mRemoteDevice.prepare(streamId);
+ } catch (CameraRuntimeException e) {
+ throw e.asChecked();
+ } catch (RemoteException e) {
+ // impossible
+ return;
+ }
+ }
+ }
+
public int capture(CaptureRequest request, CaptureCallback callback, Handler handler)
throws CameraAccessException {
if (DEBUG) {
@@ -810,6 +920,40 @@ public class CameraDeviceImpl extends CameraDevice {
}
}
+ 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>
@@ -935,6 +1079,14 @@ public class CameraDeviceImpl extends CameraDevice {
public void onIdle(CameraDevice camera) {
// Default empty implementation
}
+
+ /**
+ * The method called when the camera device has finished preparing
+ * an output Surface
+ */
+ public void onSurfacePrepared(Surface surface) {
+ // Default empty implementation
+ }
}
static class CaptureCallbackHolder {
@@ -996,19 +1148,46 @@ public class CameraDeviceImpl extends CameraDevice {
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 +1196,21 @@ public class CameraDeviceImpl extends CameraDevice {
* 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 +1224,13 @@ public class CameraDeviceImpl extends CameraDevice {
* @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 +1269,112 @@ public class CameraDeviceImpl extends CameraDevice {
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) {
-
- // 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;
- }
+ 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;
+ }
- 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 +1385,40 @@ public class CameraDeviceImpl extends CameraDevice {
}
}
}
+ }
+
+ // 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));
- }
+ // 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));
+ }
- 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);
+ 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);
- }
-
+ }
+ };
+ holder.getHandler().post(resultDispatch);
}
}
}
@@ -1319,9 +1583,11 @@ public class CameraDeviceImpl extends CameraDevice {
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 +1597,8 @@ public class CameraDeviceImpl extends CameraDevice {
+ frameNumber);
}
- mFrameNumberTracker.updateTracker(frameNumber, /*result*/null, isPartialResult);
+ mFrameNumberTracker.updateTracker(frameNumber, /*result*/null, isPartialResult,
+ isReprocess);
return;
}
@@ -1343,11 +1610,11 @@ public class CameraDeviceImpl extends CameraDevice {
+ 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 +1665,7 @@ public class CameraDeviceImpl extends CameraDevice {
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) {
@@ -1407,6 +1674,31 @@ public class CameraDeviceImpl extends CameraDevice {
}
}
+ @Override
+ public void onPrepared(int streamId) {
+ final OutputConfiguration output;
+ final StateCallbackKK sessionCallback;
+
+ if (DEBUG) {
+ Log.v(TAG, "Stream " + streamId + " is prepared");
+ }
+
+ synchronized(mInterfaceLock) {
+ output = mConfiguredOutputs.get(streamId);
+ sessionCallback = mSessionStateCallback;
+ }
+
+ if (sessionCallback == null) return;
+
+ if (output == null) {
+ Log.w(TAG, "onPrepared invoked for unknown output Surface");
+ return;
+ }
+ final Surface surface = output.getSurface();
+
+ sessionCallback.onSurfacePrepared(surface);
+ }
+
/**
* Called by onDeviceError for handling single-capture failures.
*/
@@ -1460,7 +1752,7 @@ public class CameraDeviceImpl extends CameraDevice {
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..abe26ea 100644
--- a/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java
+++ b/core/java/android/hardware/camera2/legacy/CameraDeviceUserShim.java
@@ -252,6 +252,11 @@ public class CameraDeviceUserShim implements ICameraDeviceUser {
}
@Override
+ public void onPrepared(int streamId) {
+ // TODO
+ }
+
+ @Override
public IBinder asBinder() {
// This is solely intended to be used for in-process binding.
return null;
@@ -530,6 +535,18 @@ public class CameraDeviceUserShim implements ICameraDeviceUser {
}
@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.");
@@ -605,6 +622,19 @@ public class CameraDeviceUserShim implements ICameraDeviceUser {
return CameraBinderDecorator.NO_ERROR;
}
+ public int prepare(int streamId) {
+ if (DEBUG) {
+ Log.d(TAG, "prepare called.");
+ }
+ if (mLegacyDevice.isClosed()) {
+ Log.e(TAG, "Cannot prepare stream, device has been closed.");
+ return CameraBinderDecorator.ENODEV;
+ }
+
+ // TODO: Implement and fire callback
+ return CameraBinderDecorator.NO_ERROR;
+ }
+
@Override
public IBinder asBinder() {
// This is solely intended to be used for in-process binding.
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/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index 6b4bc1e..b757a9a 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -56,7 +56,7 @@ public class FingerprintManager {
private static final boolean DEBUG = true;
private static final int MSG_ENROLL_RESULT = 100;
private static final int MSG_ACQUIRED = 101;
- private static final int MSG_PROCESSED = 102;
+ private static final int MSG_AUTHENTICATED = 102;
private static final int MSG_ERROR = 103;
private static final int MSG_REMOVED = 104;
@@ -103,6 +103,11 @@ public class FingerprintManager {
*/
public static final int FINGERPRINT_ERROR_UNABLE_TO_REMOVE = 6;
+ /**
+ * The operation was canceled because the API is locked out due to too many attempts.
+ */
+ public static final int FINGERPRINT_ERROR_LOCKOUT = 7;
+
/**
* Hardware vendors may extend this list if there are conditions that do not fall under one of
* the above categories. Vendors are responsible for providing error strings for these errors.
@@ -169,15 +174,9 @@ public class FingerprintManager {
private Fingerprint mRemovalFingerprint;
private class OnEnrollCancelListener implements OnCancelListener {
- private long mChallenge;
-
- public OnEnrollCancelListener(long challenge) {
- mChallenge = challenge;
- }
-
@Override
public void onCancel() {
- cancelEnrollment(mChallenge);
+ cancelEnrollment();
}
}
@@ -380,6 +379,26 @@ public class FingerprintManager {
*/
public void authenticate(@Nullable CryptoObject crypto, @Nullable CancellationSignal cancel,
@NonNull AuthenticationCallback callback, int flags) {
+ authenticate(crypto, cancel, callback, flags, UserHandle.myUserId());
+ }
+
+ /**
+ * Request authentication of a crypto object. This call warms up the fingerprint hardware
+ * and starts scanning for a fingerprint. It terminates when
+ * {@link AuthenticationCallback#onAuthenticationError(int, CharSequence)} or
+ * {@link AuthenticationCallback#onAuthenticationSucceeded(AuthenticationResult) is called, at
+ * which point the object is no longer valid. The operation can be canceled by using the
+ * provided cancel object.
+ *
+ * @param crypto object associated with the call or null if none required.
+ * @param cancel an object that can be used to cancel authentication
+ * @param callback an object to receive authentication events
+ * @param flags optional flags; should be 0
+ * @param userId the userId the fingerprint belongs to
+ * @hide
+ */
+ public void authenticate(@Nullable CryptoObject crypto, @Nullable CancellationSignal cancel,
+ @NonNull AuthenticationCallback callback, int flags, int userId) {
if (callback == null) {
throw new IllegalArgumentException("Must supply an authentication callback");
}
@@ -397,7 +416,7 @@ public class FingerprintManager {
mAuthenticationCallback = callback;
mCryptoObject = crypto;
long sessionId = crypto != null ? crypto.getOpId() : 0;
- mService.authenticate(mToken, sessionId, getCurrentUserId(), mServiceReceiver, flags);
+ mService.authenticate(mToken, sessionId, userId, mServiceReceiver, flags);
} catch (RemoteException e) {
Log.w(TAG, "Remote exception while authenticating: ", e);
if (callback != null) {
@@ -417,14 +436,14 @@ public class FingerprintManager {
* {@link EnrollmentCallback#onEnrollmentProgress(int) is called with remaining == 0, at
* which point the object is no longer valid. The operation can be canceled by using the
* provided cancel object.
- * @param challenge a unique id provided by a recent verification of device credentials
- * (e.g. pin, pattern or password).
+ * @param token a unique token provided by a recent creation or verification of device
+ * credentials (e.g. pin, pattern or password).
* @param cancel an object that can be used to cancel enrollment
* @param callback an object to receive enrollment events
* @param flags optional flags
* @hide
*/
- public void enroll(long challenge, CancellationSignal cancel, EnrollmentCallback callback,
+ public void enroll(byte [] token, CancellationSignal cancel, EnrollmentCallback callback,
int flags) {
if (callback == null) {
throw new IllegalArgumentException("Must supply an enrollment callback");
@@ -435,13 +454,13 @@ public class FingerprintManager {
Log.w(TAG, "enrollment already canceled");
return;
} else {
- cancel.setOnCancelListener(new OnEnrollCancelListener(challenge));
+ cancel.setOnCancelListener(new OnEnrollCancelListener());
}
}
if (mService != null) try {
mEnrollmentCallback = callback;
- mService.enroll(mToken, challenge, getCurrentUserId(), mServiceReceiver, flags);
+ mService.enroll(mToken, token, getCurrentUserId(), mServiceReceiver, flags);
} catch (RemoteException e) {
Log.w(TAG, "Remote exception in enroll: ", e);
if (callback != null) {
@@ -554,8 +573,8 @@ public class FingerprintManager {
case MSG_ACQUIRED:
sendAcquiredResult((Long) msg.obj /* deviceId */, msg.arg1 /* acquire info */);
break;
- case MSG_PROCESSED:
- sendProcessedResult((Fingerprint) msg.obj);
+ case MSG_AUTHENTICATED:
+ sendAuthenticatedResult((Fingerprint) msg.obj);
break;
case MSG_ERROR:
sendErrorResult((Long) msg.obj /* deviceId */, msg.arg1 /* errMsgId */);
@@ -597,7 +616,7 @@ public class FingerprintManager {
}
}
- private void sendProcessedResult(Fingerprint fp) {
+ private void sendAuthenticatedResult(Fingerprint fp) {
if (mAuthenticationCallback != null) {
if (fp.getFingerId() == 0 && fp.getGroupId() == 0) {
// Fingerprint template valid but doesn't match one in database
@@ -647,7 +666,7 @@ public class FingerprintManager {
mRemovalCallback = null;
}
- private void cancelEnrollment(long challenge) {
+ private void cancelEnrollment() {
if (mService != null) try {
mService.cancelEnrollment(mToken);
} catch (RemoteException e) {
@@ -675,8 +694,11 @@ public class FingerprintManager {
return mContext.getString(
com.android.internal.R.string.fingerprint_error_no_space);
case FINGERPRINT_ERROR_TIMEOUT:
- return mContext.getString(
- com.android.internal.R.string.fingerprint_error_timeout);
+ return mContext.getString(com.android.internal.R.string.fingerprint_error_timeout);
+ case FINGERPRINT_ERROR_CANCELED:
+ return mContext.getString(com.android.internal.R.string.fingerprint_error_canceled);
+ case FINGERPRINT_ERROR_LOCKOUT:
+ return mContext.getString(com.android.internal.R.string.fingerprint_error_lockout);
default:
if (errMsg >= FINGERPRINT_ERROR_VENDOR_BASE) {
int msgNumber = errMsg - FINGERPRINT_ERROR_VENDOR_BASE;
@@ -733,8 +755,8 @@ public class FingerprintManager {
mHandler.obtainMessage(MSG_ACQUIRED, acquireInfo, 0, deviceId).sendToTarget();
}
- public void onProcessed(long deviceId, int fingerId, int groupId) {
- mHandler.obtainMessage(MSG_PROCESSED,
+ public void onAuthenticated(long deviceId, int fingerId, int groupId) {
+ mHandler.obtainMessage(MSG_AUTHENTICATED,
new Fingerprint(null, groupId, fingerId, deviceId)).sendToTarget();
}
diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
index 2fcb20e..6fe72d5 100644
--- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl
+++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
@@ -33,7 +33,7 @@ interface IFingerprintService {
void cancelAuthentication(IBinder token);
// Start fingerprint enrollment
- void enroll(IBinder token, long challenge, int groupId, IFingerprintServiceReceiver receiver,
+ void enroll(IBinder token, in byte [] cryptoToken, int groupId, IFingerprintServiceReceiver receiver,
int flags);
// Cancel enrollment in progress
diff --git a/core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl b/core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl
index e82395f..a2d74b8 100644
--- a/core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl
+++ b/core/java/android/hardware/fingerprint/IFingerprintServiceReceiver.aidl
@@ -25,7 +25,7 @@ import android.os.UserHandle;
oneway interface IFingerprintServiceReceiver {
void onEnrollResult(long deviceId, int fingerId, int groupId, int remaining);
void onAcquired(long deviceId, int acquiredInfo);
- void onProcessed(long deviceId, int fingerId, int groupId);
+ void onAuthenticated(long deviceId, int fingerId, int groupId);
void onError(long deviceId, int error);
void onRemoved(long deviceId, int fingerId, int groupId);
}
diff --git a/core/java/android/hardware/location/GeofenceHardwareImpl.java b/core/java/android/hardware/location/GeofenceHardwareImpl.java
index 5d40e94..ee2d43c 100644
--- a/core/java/android/hardware/location/GeofenceHardwareImpl.java
+++ b/core/java/android/hardware/location/GeofenceHardwareImpl.java
@@ -41,6 +41,7 @@ import java.util.Iterator;
public final class GeofenceHardwareImpl {
private static final String TAG = "GeofenceHardwareImpl";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+ private static final int FIRST_VERSION_WITH_CAPABILITIES = 2;
private final Context mContext;
private static GeofenceHardwareImpl sInstance;
@@ -54,6 +55,7 @@ public final class GeofenceHardwareImpl {
private IFusedGeofenceHardware mFusedService;
private IGpsGeofenceHardware mGpsService;
private int mCapabilities;
+ private int mVersion = 1;
private int[] mSupportedMonitorTypes = new int[GeofenceHardware.NUM_MONITORS];
@@ -145,8 +147,10 @@ public final class GeofenceHardwareImpl {
private void updateFusedHardwareAvailability() {
boolean fusedSupported;
try {
+ final boolean hasGnnsCapabilities = (mVersion < FIRST_VERSION_WITH_CAPABILITIES)
+ || (mCapabilities & CAPABILITY_GNSS) != 0;
fusedSupported = (mFusedService != null
- ? mFusedService.isSupported() && (mCapabilities & CAPABILITY_GNSS) != 0
+ ? mFusedService.isSupported() && hasGnnsCapabilities
: false);
} catch (RemoteException e) {
Log.e(TAG, "RemoteException calling LocationManagerService");
@@ -177,6 +181,11 @@ public final class GeofenceHardwareImpl {
updateFusedHardwareAvailability();
}
+ public void setVersion(int version) {
+ mVersion = version;
+ updateFusedHardwareAvailability();
+ }
+
public void setFusedGeofenceHardware(IFusedGeofenceHardware service) {
if(mFusedService == null) {
mFusedService = service;
@@ -230,7 +239,12 @@ public final class GeofenceHardwareImpl {
case GeofenceHardware.MONITORING_TYPE_GPS_HARDWARE:
return CAPABILITY_GNSS;
case GeofenceHardware.MONITORING_TYPE_FUSED_HARDWARE:
- return mCapabilities;
+ if (mVersion >= FIRST_VERSION_WITH_CAPABILITIES) {
+ return mCapabilities;
+ }
+ // This was the implied capability on old FLP HAL versions that didn't
+ // have the capability callback.
+ return CAPABILITY_GNSS;
}
break;
}
diff --git a/core/java/android/hardware/location/IFusedLocationHardware.aidl b/core/java/android/hardware/location/IFusedLocationHardware.aidl
index 3de766a..2ea4d23 100644
--- a/core/java/android/hardware/location/IFusedLocationHardware.aidl
+++ b/core/java/android/hardware/location/IFusedLocationHardware.aidl
@@ -121,4 +121,9 @@ interface IFusedLocationHardware {
* of the locations returned in this call.
*/
void flushBatchedLocations() = 11;
+
+ /**
+ * Returns the version of this FLP HAL implementation.
+ */
+ int getVersion() = 12;
}
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index da2c5e0..55e39b1 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -286,6 +286,14 @@ public class ConnectivityManager {
public static final String EXTRA_IS_CAPTIVE_PORTAL = "captivePortal";
/**
+ * Action used to display a dialog that asks the user whether to connect to a network that is
+ * not validated. This intent is used to start the dialog in settings via startActivity.
+ *
+ * @hide
+ */
+ public static final String ACTION_PROMPT_UNVALIDATED = "android.net.conn.PROMPT_UNVALIDATED";
+
+ /**
* The absence of a connection type.
* @hide
*/
@@ -2455,6 +2463,29 @@ public class ConnectivityManager {
}
/**
+ * Informs the system whether it should switch to {@code network} regardless of whether it is
+ * validated or not. If {@code accept} is true, and the network was explicitly selected by the
+ * user (e.g., by selecting a Wi-Fi network in the Settings app), then the network will become
+ * the system default network regardless of any other network that's currently connected. If
+ * {@code always} is true, then the choice is remembered, so that the next time the user
+ * connects to this network, the system will switch to it.
+ *
+ * <p>This method requires the caller to hold the permission
+ * {@link android.Manifest.permission#CONNECTIVITY_INTERNAL}
+ *
+ * @param network The network to accept.
+ * @param accept Whether to accept the network even if unvalidated.
+ * @param always Whether to remember this choice in the future.
+ *
+ * @hide
+ */
+ public void setAcceptUnvalidated(Network network, boolean accept, boolean always) {
+ try {
+ mService.setAcceptUnvalidated(network, accept, always);
+ } catch (RemoteException e) {}
+ }
+
+ /**
* Resets all connectivity manager settings back to factory defaults.
* @hide
*/
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index 3c09978..1aa9c45 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -156,6 +156,8 @@ interface IConnectivityManager
void releaseNetworkRequest(in NetworkRequest networkRequest);
+ void setAcceptUnvalidated(in Network network, boolean accept, boolean always);
+
int getRestoreDefaultNetworkDelay(int networkType);
boolean addVpnAddress(String address, int prefixLength);
diff --git a/core/java/android/net/Network.java b/core/java/android/net/Network.java
index 65d325a1..67ecb5d 100644
--- a/core/java/android/net/Network.java
+++ b/core/java/android/net/Network.java
@@ -340,6 +340,35 @@ public class Network implements Parcelable {
}
}
+ /**
+ * Returns a handle representing this {@code Network}, for use with the NDK API.
+ */
+ public long getNetworkHandle() {
+ // The network handle is explicitly not the same as the netId.
+ //
+ // The netId is an implementation detail which might be changed in the
+ // future, or which alone (i.e. in the absence of some additional
+ // context) might not be sufficient to fully identify a Network.
+ //
+ // As such, the intention is to prevent accidental misuse of the API
+ // that might result if a developer assumed that handles and netIds
+ // were identical and passing a netId to a call expecting a handle
+ // "just worked". Such accidental misuse, if widely deployed, might
+ // prevent future changes to the semantics of the netId field or
+ // inhibit the expansion of state required for Network objects.
+ //
+ // This extra layer of indirection might be seen as paranoia, and might
+ // never end up being necessary, but the added complexity is trivial.
+ // At some future date it may be desirable to realign the handle with
+ // Multiple Provisioning Domains API recommendations, as made by the
+ // IETF mif working group.
+ //
+ // The HANDLE_MAGIC value MUST be kept in sync with the corresponding
+ // value in the native/android/net.c NDK implementation.
+ final long HANDLE_MAGIC = 0xfacade;
+ return (((long) netId) << 32) | HANDLE_MAGIC;
+ }
+
// implement the Parcelable interface
public int describeContents() {
return 0;
diff --git a/core/java/android/net/NetworkAgent.java b/core/java/android/net/NetworkAgent.java
index 24aaf0d..95ceb2a 100644
--- a/core/java/android/net/NetworkAgent.java
+++ b/core/java/android/net/NetworkAgent.java
@@ -104,7 +104,7 @@ public abstract class NetworkAgent extends Handler {
public static final int EVENT_UID_RANGES_REMOVED = BASE + 6;
/**
- * Sent by ConnectivitySerice to the NetworkAgent to inform the agent of the
+ * Sent by ConnectivityService to the NetworkAgent to inform the agent of the
* networks status - whether we could use the network or could not, due to
* either a bad network configuration (no internet link) or captive portal.
*
@@ -119,9 +119,21 @@ public abstract class NetworkAgent extends Handler {
* Sent by the NetworkAgent to ConnectivityService to indicate this network was
* explicitly selected. This should be sent before the NetworkInfo is marked
* CONNECTED so it can be given special treatment at that time.
+ *
+ * obj = boolean indicating whether to use this network even if unvalidated
*/
public static final int EVENT_SET_EXPLICITLY_SELECTED = BASE + 8;
+ /**
+ * Sent by ConnectivityService to the NetworkAgent to inform the agent of
+ * whether the network should in the future be used even if not validated.
+ * This decision is made by the user, but it is the network transport's
+ * responsibility to remember it.
+ *
+ * arg1 = 1 if true, 0 if false
+ */
+ public static final int CMD_SAVE_ACCEPT_UNVALIDATED = BASE + 9;
+
public NetworkAgent(Looper looper, Context context, String logTag, NetworkInfo ni,
NetworkCapabilities nc, LinkProperties lp, int score) {
this(looper, context, logTag, ni, nc, lp, score, null);
@@ -191,6 +203,9 @@ public abstract class NetworkAgent extends Handler {
networkStatus(msg.arg1);
break;
}
+ case CMD_SAVE_ACCEPT_UNVALIDATED: {
+ saveAcceptUnvalidated(msg.arg1 != 0);
+ }
}
}
@@ -258,10 +273,16 @@ public abstract class NetworkAgent extends Handler {
/**
* Called by the bearer to indicate this network was manually selected by the user.
* This should be called before the NetworkInfo is marked CONNECTED so that this
- * Network can be given special treatment at that time.
+ * Network can be given special treatment at that time. If {@code acceptUnvalidated} is
+ * {@code true}, then the system will switch to this network. If it is {@code false} and the
+ * network cannot be validated, the system will ask the user whether to switch to this network.
+ * If the user confirms and selects "don't ask again", then the system will call
+ * {@link #saveAcceptUnvalidated} to persist the user's choice. Thus, if the transport ever
+ * calls this method with {@code acceptUnvalidated} set to {@code false}, it must also implement
+ * {@link #saveAcceptUnvalidated} to respect the user's choice.
*/
- public void explicitlySelected() {
- queueOrSendMessage(EVENT_SET_EXPLICITLY_SELECTED, 0);
+ public void explicitlySelected(boolean acceptUnvalidated) {
+ queueOrSendMessage(EVENT_SET_EXPLICITLY_SELECTED, acceptUnvalidated);
}
/**
@@ -290,6 +311,16 @@ public abstract class NetworkAgent extends Handler {
protected void networkStatus(int status) {
}
+ /**
+ * Called when the user asks to remember the choice to use this network even if unvalidated.
+ * The transport is responsible for remembering the choice, and the next time the user connects
+ * to the network, should explicitlySelected with {@code acceptUnvalidated} set to {@code true}.
+ * This method will only be called if {@link #explicitlySelected} was called with
+ * {@code acceptUnvalidated} set to {@code false}.
+ */
+ protected void saveAcceptUnvalidated(boolean accept) {
+ }
+
protected void log(String s) {
Log.d(LOG_TAG, "NetworkAgent: " + s);
}
diff --git a/core/java/android/net/NetworkMisc.java b/core/java/android/net/NetworkMisc.java
index b92c9e3..5511a24 100644
--- a/core/java/android/net/NetworkMisc.java
+++ b/core/java/android/net/NetworkMisc.java
@@ -45,6 +45,13 @@ public class NetworkMisc implements Parcelable {
public boolean explicitlySelected;
/**
+ * Set if the user desires to use this network even if it is unvalidated. This field has meaning
+ * only if {#link explicitlySelected} is true. If it is, this field must also be set to the
+ * appropriate value based on previous user choice.
+ */
+ public boolean acceptUnvalidated;
+
+ /**
* For mobile networks, this is the subscriber ID (such as IMSI).
*/
public String subscriberId;
@@ -56,6 +63,7 @@ public class NetworkMisc implements Parcelable {
if (nm != null) {
allowBypass = nm.allowBypass;
explicitlySelected = nm.explicitlySelected;
+ acceptUnvalidated = nm.acceptUnvalidated;
subscriberId = nm.subscriberId;
}
}
@@ -69,6 +77,7 @@ public class NetworkMisc implements Parcelable {
public void writeToParcel(Parcel out, int flags) {
out.writeInt(allowBypass ? 1 : 0);
out.writeInt(explicitlySelected ? 1 : 0);
+ out.writeInt(acceptUnvalidated ? 1 : 0);
out.writeString(subscriberId);
}
@@ -78,6 +87,7 @@ public class NetworkMisc implements Parcelable {
NetworkMisc networkMisc = new NetworkMisc();
networkMisc.allowBypass = in.readInt() != 0;
networkMisc.explicitlySelected = in.readInt() != 0;
+ networkMisc.acceptUnvalidated = in.readInt() != 0;
networkMisc.subscriberId = in.readString();
return networkMisc;
}
diff --git a/core/java/android/nfc/cardemulation/ApduServiceInfo.java b/core/java/android/nfc/cardemulation/ApduServiceInfo.java
index 00b2ee3..f10e530 100644
--- a/core/java/android/nfc/cardemulation/ApduServiceInfo.java
+++ b/core/java/android/nfc/cardemulation/ApduServiceInfo.java
@@ -40,6 +40,7 @@ import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
/**
@@ -262,7 +263,7 @@ public final class ApduServiceInfo implements Parcelable {
* for that category.
* @return List of AIDs registered by the service
*/
- public ArrayList<String> getAids() {
+ public List<String> getAids() {
final ArrayList<String> aids = new ArrayList<String>();
for (AidGroup group : getAidGroups()) {
aids.addAll(group.aids);
@@ -270,6 +271,18 @@ public final class ApduServiceInfo implements Parcelable {
return aids;
}
+ public List<String> getPrefixAids() {
+ final ArrayList<String> prefixAids = new ArrayList<String>();
+ for (AidGroup group : getAidGroups()) {
+ for (String aid : group.aids) {
+ if (aid.endsWith("*")) {
+ prefixAids.add(aid);
+ }
+ }
+ }
+ return prefixAids;
+ }
+
/**
* Returns the registered AID group for this category.
*/
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 abstract class BatteryStats implements Parcelable {
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 @@ public abstract class BatteryStats implements Parcelable {
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 abstract class BatteryStats implements Parcelable {
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/os/Binder.java b/core/java/android/os/Binder.java
index b4a4624..64562a4 100644
--- a/core/java/android/os/Binder.java
+++ b/core/java/android/os/Binder.java
@@ -187,6 +187,13 @@ public class Binder implements IBinder {
}
/**
+ * Call blocks until the number of executing binder threads is less
+ * than the maximum number of binder threads allowed for this process.
+ * @hide
+ */
+ public static final native void blockUntilThreadAvailable();
+
+ /**
* Default constructor initializes the object.
*/
public Binder() {
diff --git a/core/java/android/os/Debug.java b/core/java/android/os/Debug.java
index 512e212..75b1101 100644
--- a/core/java/android/os/Debug.java
+++ b/core/java/android/os/Debug.java
@@ -34,6 +34,7 @@ import java.lang.annotation.Target;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.Map;
import org.apache.harmony.dalvik.ddmc.Chunk;
import org.apache.harmony.dalvik.ddmc.ChunkHandler;
@@ -1101,6 +1102,95 @@ href="{@docRoot}guide/developing/tools/traceview.html">Traceview: A Graphical Lo
}
/**
+ * Returns the value of a particular runtime statistic or {@code null} if no
+ * such runtime statistic exists.
+ *
+ * <p>The following table lists the runtime statistics that the runtime supports.
+ * Note runtime statistics may be added or removed in a future API level.</p>
+ *
+ * <table>
+ * <thead>
+ * <tr>
+ * <th>Runtime statistic name</th>
+ * <th>Meaning</th>
+ * <th>Example</th>
+ * <th>Supported (API Levels)</th>
+ * </tr>
+ * </thead>
+ * <tbody>
+ * <tr>
+ * <td>art.gc.gc-count</td>
+ * <td>The number of garbage collection runs.</td>
+ * <td>{@code 164}</td>
+ * <td>23</td>
+ * </tr>
+ * <tr>
+ * <td>art.gc.gc-time</td>
+ * <td>The total duration of garbage collection runs in ms.</td>
+ * <td>{@code 62364}</td>
+ * <td>23</td>
+ * </tr>
+ * <tr>
+ * <td>art.gc.bytes-allocated</td>
+ * <td>The total number of bytes that the application allocated.</td>
+ * <td>{@code 1463948408}</td>
+ * <td>23</td>
+ * </tr>
+ * <tr>
+ * <td>art.gc.bytes-freed</td>
+ * <td>The total number of bytes that garbage collection reclaimed.</td>
+ * <td>{@code 1313493084}</td>
+ * <td>23</td>
+ * </tr>
+ * <tr>
+ * <td>art.gc.blocking-gc-count</td>
+ * <td>The number of blocking garbage collection runs.</td>
+ * <td>{@code 2}</td>
+ * <td>23</td>
+ * </tr>
+ * <tr>
+ * <td>art.gc.blocking-gc-time</td>
+ * <td>The total duration of blocking garbage collection runs in ms.</td>
+ * <td>{@code 804}</td>
+ * <td>23</td>
+ * </tr>
+ * <tr>
+ * <td>art.gc.gc-count-rate-histogram</td>
+ * <td>The histogram of the number of garbage collection runs per 10 seconds.</td>
+ * <td>{@code 0:34503,1:45350,2:11281,3:8088,4:43,5:8}</td>
+ * <td>23</td>
+ * </tr>
+ * <tr>
+ * <td>art.gc.blocking-gc-count-rate-histogram</td>
+ * <td>The histogram of the number of garbage collection runs per 10 seconds.</td>
+ * <td>{@code 0:99269,1:1,2:1}</td>
+ * <td>23</td>
+ * </tr>
+ * </tbody>
+ * </table>
+ *
+ * @param statName
+ * the name of the runtime statistic to look up.
+ * @return the value of the specified runtime statistic or {@code null} if the
+ * runtime statistic doesn't exist.
+ * @hide
+ */
+ public static String getRuntimeStat(String statName) {
+ return VMDebug.getRuntimeStat(statName);
+ }
+
+ /**
+ * Returns a map of the names/values of the runtime statistics
+ * that {@link #getRuntimeStat(String)} supports.
+ *
+ * @return a map of the names/values of the supported runtime statistics.
+ * @hide
+ */
+ public static Map<String, String> getRuntimeStats() {
+ return VMDebug.getRuntimeStats();
+ }
+
+ /**
* Returns the size of the native heap.
* @return The size of the native heap in bytes.
*/
diff --git a/core/java/android/os/storage/DiskInfo.java b/core/java/android/os/storage/DiskInfo.java
index 4704b67..dc96640 100644
--- a/core/java/android/os/storage/DiskInfo.java
+++ b/core/java/android/os/storage/DiskInfo.java
@@ -16,6 +16,7 @@
package android.os.storage;
+import android.annotation.NonNull;
import android.content.res.Resources;
import android.os.Parcel;
import android.os.Parcelable;
@@ -33,6 +34,8 @@ import java.io.CharArrayWriter;
* @hide
*/
public class DiskInfo implements Parcelable {
+ public static final String EXTRA_DISK_ID = "android.os.storage.extra.DISK_ID";
+
public static final int FLAG_ADOPTABLE = 1 << 0;
public static final int FLAG_DEFAULT_PRIMARY = 1 << 1;
public static final int FLAG_SD = 1 << 2;
@@ -42,7 +45,7 @@ public class DiskInfo implements Parcelable {
public final int flags;
public long size;
public String label;
- public String[] volumes;
+ public String[] volumeIds;
public DiskInfo(String id, int flags) {
this.id = Preconditions.checkNotNull(id);
@@ -54,7 +57,11 @@ public class DiskInfo implements Parcelable {
flags = parcel.readInt();
size = parcel.readLong();
label = parcel.readString();
- volumes = parcel.readStringArray();
+ volumeIds = parcel.readStringArray();
+ }
+
+ public @NonNull String getId() {
+ return id;
}
public String getDescription() {
@@ -68,6 +75,18 @@ public class DiskInfo implements Parcelable {
}
}
+ public boolean isSd() {
+ return (flags & FLAG_SD) != 0;
+ }
+
+ public boolean isUsb() {
+ return (flags & FLAG_USB) != 0;
+ }
+
+ public boolean isAdoptable() {
+ return (flags & FLAG_ADOPTABLE) != 0;
+ }
+
@Override
public String toString() {
final CharArrayWriter writer = new CharArrayWriter();
@@ -82,7 +101,7 @@ public class DiskInfo implements Parcelable {
pw.printPair("flags", DebugUtils.flagsToString(getClass(), "FLAG_", flags));
pw.printPair("size", size);
pw.printPair("label", label);
- pw.printPair("volumes", volumes);
+ pw.printPair("volumeIds", volumeIds);
pw.decreaseIndent();
pw.println();
}
@@ -122,6 +141,6 @@ public class DiskInfo implements Parcelable {
parcel.writeInt(this.flags);
parcel.writeLong(size);
parcel.writeString(label);
- parcel.writeStringArray(volumes);
+ parcel.writeStringArray(volumeIds);
}
}
diff --git a/core/java/android/os/storage/IMountService.java b/core/java/android/os/storage/IMountService.java
index 10ffd48..0a8187e 100644
--- a/core/java/android/os/storage/IMountService.java
+++ b/core/java/android/os/storage/IMountService.java
@@ -923,12 +923,13 @@ public interface IMountService extends IInterface {
}
@Override
- public VolumeInfo[] getVolumes() throws RemoteException {
+ public VolumeInfo[] getVolumes(int _flags) throws RemoteException {
Parcel _data = Parcel.obtain();
Parcel _reply = Parcel.obtain();
VolumeInfo[] _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
+ _data.writeInt(_flags);
mRemote.transact(Stub.TRANSACTION_getVolumes, _data, _reply, 0);
_reply.readException();
_result = _reply.createTypedArray(VolumeInfo.CREATOR);
@@ -1029,6 +1030,39 @@ public interface IMountService extends IInterface {
_data.recycle();
}
}
+
+ @Override
+ public void setVolumeNickname(String volId, String nickname) throws RemoteException {
+ Parcel _data = Parcel.obtain();
+ Parcel _reply = Parcel.obtain();
+ try {
+ _data.writeInterfaceToken(DESCRIPTOR);
+ _data.writeString(volId);
+ _data.writeString(nickname);
+ mRemote.transact(Stub.TRANSACTION_setVolumeNickname, _data, _reply, 0);
+ _reply.readException();
+ } finally {
+ _reply.recycle();
+ _data.recycle();
+ }
+ }
+
+ @Override
+ public void setVolumeUserFlags(String volId, int flags, int mask) throws RemoteException {
+ Parcel _data = Parcel.obtain();
+ Parcel _reply = Parcel.obtain();
+ try {
+ _data.writeInterfaceToken(DESCRIPTOR);
+ _data.writeString(volId);
+ _data.writeInt(flags);
+ _data.writeInt(mask);
+ mRemote.transact(Stub.TRANSACTION_setVolumeUserFlags, _data, _reply, 0);
+ _reply.readException();
+ } finally {
+ _reply.recycle();
+ _data.recycle();
+ }
+ }
}
private static final String DESCRIPTOR = "IMountService";
@@ -1132,6 +1166,9 @@ public interface IMountService extends IInterface {
static final int TRANSACTION_partitionPrivate = IBinder.FIRST_CALL_TRANSACTION + 50;
static final int TRANSACTION_partitionMixed = IBinder.FIRST_CALL_TRANSACTION + 51;
+ static final int TRANSACTION_setVolumeNickname = IBinder.FIRST_CALL_TRANSACTION + 52;
+ static final int TRANSACTION_setVolumeUserFlags = IBinder.FIRST_CALL_TRANSACTION + 53;
+
/**
* Cast an IBinder object into an IMountService interface, generating a
* proxy if needed.
@@ -1566,7 +1603,8 @@ public interface IMountService extends IInterface {
}
case TRANSACTION_getVolumes: {
data.enforceInterface(DESCRIPTOR);
- VolumeInfo[] volumes = getVolumes();
+ int _flags = data.readInt();
+ VolumeInfo[] volumes = getVolumes(_flags);
reply.writeNoException();
reply.writeTypedArray(volumes, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
return true;
@@ -1614,6 +1652,23 @@ public interface IMountService extends IInterface {
reply.writeNoException();
return true;
}
+ case TRANSACTION_setVolumeNickname: {
+ data.enforceInterface(DESCRIPTOR);
+ String volId = data.readString();
+ String nickname = data.readString();
+ setVolumeNickname(volId, nickname);
+ reply.writeNoException();
+ return true;
+ }
+ case TRANSACTION_setVolumeUserFlags: {
+ data.enforceInterface(DESCRIPTOR);
+ String volId = data.readString();
+ int _flags = data.readInt();
+ int _mask = data.readInt();
+ setVolumeUserFlags(volId, _flags, _mask);
+ reply.writeNoException();
+ return true;
+ }
}
return super.onTransact(code, data, reply, flags);
}
@@ -1902,7 +1957,7 @@ public interface IMountService extends IInterface {
public void waitForAsecScan() throws RemoteException;
public DiskInfo[] getDisks() throws RemoteException;
- public VolumeInfo[] getVolumes() throws RemoteException;
+ public VolumeInfo[] getVolumes(int flags) throws RemoteException;
public void mount(String volId) throws RemoteException;
public void unmount(String volId) throws RemoteException;
@@ -1911,4 +1966,7 @@ public interface IMountService extends IInterface {
public void partitionPublic(String diskId) throws RemoteException;
public void partitionPrivate(String diskId) throws RemoteException;
public void partitionMixed(String diskId, int ratio) throws RemoteException;
+
+ public void setVolumeNickname(String volId, String nickname) throws RemoteException;
+ public void setVolumeUserFlags(String volId, int flags, int mask) throws RemoteException;
}
diff --git a/core/java/android/os/storage/IMountServiceListener.java b/core/java/android/os/storage/IMountServiceListener.java
index 3965f9d..fd914bc 100644
--- a/core/java/android/os/storage/IMountServiceListener.java
+++ b/core/java/android/os/storage/IMountServiceListener.java
@@ -91,6 +91,13 @@ public interface IMountServiceListener extends IInterface {
reply.writeNoException();
return true;
}
+ case TRANSACTION_onVolumeMetadataChanged: {
+ data.enforceInterface(DESCRIPTOR);
+ final VolumeInfo vol = (VolumeInfo) data.readParcelable(null);
+ onVolumeMetadataChanged(vol);
+ reply.writeNoException();
+ return true;
+ }
}
return super.onTransact(code, data, reply, flags);
}
@@ -175,6 +182,22 @@ public interface IMountServiceListener extends IInterface {
_data.recycle();
}
}
+
+ @Override
+ public void onVolumeMetadataChanged(VolumeInfo vol) throws RemoteException {
+ Parcel _data = Parcel.obtain();
+ Parcel _reply = Parcel.obtain();
+ try {
+ _data.writeInterfaceToken(DESCRIPTOR);
+ _data.writeParcelable(vol, 0);
+ mRemote.transact(Stub.TRANSACTION_onVolumeMetadataChanged, _data, _reply,
+ android.os.IBinder.FLAG_ONEWAY);
+ _reply.readException();
+ } finally {
+ _reply.recycle();
+ _data.recycle();
+ }
+ }
}
static final int TRANSACTION_onUsbMassStorageConnectionChanged = (IBinder.FIRST_CALL_TRANSACTION + 0);
@@ -182,6 +205,7 @@ public interface IMountServiceListener extends IInterface {
static final int TRANSACTION_onStorageStateChanged = (IBinder.FIRST_CALL_TRANSACTION + 1);
static final int TRANSACTION_onVolumeStateChanged = (IBinder.FIRST_CALL_TRANSACTION + 2);
+ static final int TRANSACTION_onVolumeMetadataChanged = (IBinder.FIRST_CALL_TRANSACTION + 3);
}
/**
@@ -204,4 +228,6 @@ public interface IMountServiceListener extends IInterface {
public void onVolumeStateChanged(VolumeInfo vol, int oldState, int newState)
throws RemoteException;
+
+ public void onVolumeMetadataChanged(VolumeInfo vol) throws RemoteException;
}
diff --git a/core/java/android/os/storage/StorageEventListener.java b/core/java/android/os/storage/StorageEventListener.java
index 29d5494..28a187d 100644
--- a/core/java/android/os/storage/StorageEventListener.java
+++ b/core/java/android/os/storage/StorageEventListener.java
@@ -40,4 +40,7 @@ public class StorageEventListener {
public void onVolumeStateChanged(VolumeInfo vol, int oldState, int newState) {
}
+
+ public void onVolumeMetadataChanged(VolumeInfo vol) {
+ }
}
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index bd42f6a..0e977ff 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -35,7 +35,6 @@ import android.util.Log;
import android.util.SparseArray;
import com.android.internal.os.SomeArgs;
-import com.android.internal.util.ArrayUtils;
import com.android.internal.util.Preconditions;
import java.io.File;
@@ -71,6 +70,9 @@ public class StorageManager {
/** {@hide} */
public static final String PROP_PRIMARY_PHYSICAL = "ro.vold.primary_physical";
+ /** {@hide} */
+ public static final int FLAG_ALL_METADATA = 1 << 0;
+
private final Context mContext;
private final ContentResolver mResolver;
@@ -84,6 +86,7 @@ public class StorageManager {
Handler.Callback {
private static final int MSG_STORAGE_STATE_CHANGED = 1;
private static final int MSG_VOLUME_STATE_CHANGED = 2;
+ private static final int MSG_VOLUME_METADATA_CHANGED = 3;
final StorageEventListener mCallback;
final Handler mHandler;
@@ -106,6 +109,10 @@ public class StorageManager {
mCallback.onVolumeStateChanged((VolumeInfo) args.arg1, args.argi2, args.argi3);
args.recycle();
return true;
+ case MSG_VOLUME_METADATA_CHANGED:
+ mCallback.onVolumeMetadataChanged((VolumeInfo) args.arg1);
+ args.recycle();
+ return true;
}
args.recycle();
return false;
@@ -133,6 +140,13 @@ public class StorageManager {
args.argi3 = newState;
mHandler.obtainMessage(MSG_VOLUME_STATE_CHANGED, args).sendToTarget();
}
+
+ @Override
+ public void onVolumeMetadataChanged(VolumeInfo vol) {
+ final SomeArgs args = SomeArgs.obtain();
+ args.arg1 = vol;
+ mHandler.obtainMessage(MSG_VOLUME_METADATA_CHANGED, args).sendToTarget();
+ }
}
/**
@@ -456,18 +470,6 @@ public class StorageManager {
}
/** {@hide} */
- public @Nullable DiskInfo findDiskByVolumeId(String volId) {
- Preconditions.checkNotNull(volId);
- // TODO; go directly to service to make this faster
- for (DiskInfo disk : getDisks()) {
- if (ArrayUtils.contains(disk.volumes, volId)) {
- return disk;
- }
- }
- return null;
- }
-
- /** {@hide} */
public @Nullable VolumeInfo findVolumeById(String id) {
Preconditions.checkNotNull(id);
// TODO; go directly to service to make this faster
@@ -493,25 +495,27 @@ public class StorageManager {
/** {@hide} */
public @NonNull List<VolumeInfo> getVolumes() {
+ return getVolumes(0);
+ }
+
+ /** {@hide} */
+ public @NonNull List<VolumeInfo> getVolumes(int flags) {
try {
- return Arrays.asList(mMountService.getVolumes());
+ return Arrays.asList(mMountService.getVolumes(flags));
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
}
/** {@hide} */
- public @Nullable String getBestVolumeDescription(String volId) {
- String descrip = null;
-
- final VolumeInfo vol = findVolumeById(volId);
- if (vol != null) {
- descrip = vol.getDescription();
- }
+ public @Nullable String getBestVolumeDescription(VolumeInfo vol) {
+ String descrip = vol.getDescription();
- final DiskInfo disk = findDiskByVolumeId(volId);
- if (disk != null && TextUtils.isEmpty(descrip)) {
- descrip = disk.getDescription();
+ if (vol.diskId != null) {
+ final DiskInfo disk = findDiskById(vol.diskId);
+ if (disk != null && TextUtils.isEmpty(descrip)) {
+ descrip = disk.getDescription();
+ }
}
return descrip;
@@ -572,6 +576,35 @@ public class StorageManager {
}
/** {@hide} */
+ public void setVolumeNickname(String volId, String nickname) {
+ try {
+ mMountService.setVolumeNickname(volId, nickname);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /** {@hide} */
+ public void setVolumeInited(String volId, boolean inited) {
+ try {
+ mMountService.setVolumeUserFlags(volId, inited ? VolumeInfo.USER_FLAG_INITED : 0,
+ VolumeInfo.USER_FLAG_INITED);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /** {@hide} */
+ public void setVolumeSnoozed(String volId, boolean snoozed) {
+ try {
+ mMountService.setVolumeUserFlags(volId, snoozed ? VolumeInfo.USER_FLAG_SNOOZED : 0,
+ VolumeInfo.USER_FLAG_SNOOZED);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+
+ /** {@hide} */
public @Nullable StorageVolume getStorageVolume(File file) {
return getStorageVolume(getVolumeList(), file);
}
diff --git a/core/java/android/os/storage/VolumeInfo.java b/core/java/android/os/storage/VolumeInfo.java
index beca8b8..f06fc8c 100644
--- a/core/java/android/os/storage/VolumeInfo.java
+++ b/core/java/android/os/storage/VolumeInfo.java
@@ -22,10 +22,12 @@ import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.mtp.MtpStorage;
+import android.net.Uri;
import android.os.Environment;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.UserHandle;
+import android.provider.DocumentsContract;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.DebugUtils;
@@ -45,6 +47,8 @@ import java.io.File;
* @hide
*/
public class VolumeInfo implements Parcelable {
+ public static final String EXTRA_VOLUME_ID = "android.os.storage.extra.VOLUME_ID";
+
/** Stub volume representing internal private storage */
public static final String ID_PRIVATE_INTERNAL = "private";
/** Real volume representing internal emulated storage */
@@ -67,6 +71,9 @@ public class VolumeInfo implements Parcelable {
public static final int FLAG_PRIMARY = 1 << 0;
public static final int FLAG_VISIBLE = 1 << 1;
+ public static final int USER_FLAG_INITED = 1 << 0;
+ public static final int USER_FLAG_SNOOZED = 1 << 1;
+
private static SparseArray<String> sStateToEnvironment = new SparseArray<>();
private static ArrayMap<String, String> sEnvironmentToBroadcast = new ArrayMap<>();
@@ -100,7 +107,9 @@ public class VolumeInfo implements Parcelable {
/** Framework state */
public final int mtpIndex;
+ public String diskId;
public String nickname;
+ public int userFlags = 0;
public VolumeInfo(String id, int type, int mtpIndex) {
this.id = Preconditions.checkNotNull(id);
@@ -119,7 +128,9 @@ public class VolumeInfo implements Parcelable {
fsLabel = parcel.readString();
path = parcel.readString();
mtpIndex = parcel.readInt();
+ diskId = parcel.readString();
nickname = parcel.readString();
+ userFlags = parcel.readInt();
}
public static @NonNull String getEnvironmentForState(int state) {
@@ -139,6 +150,30 @@ public class VolumeInfo implements Parcelable {
return getBroadcastForEnvironment(getEnvironmentForState(state));
}
+ public @NonNull String getId() {
+ return id;
+ }
+
+ public @Nullable String getDiskId() {
+ return diskId;
+ }
+
+ public int getType() {
+ return type;
+ }
+
+ public int getState() {
+ return state;
+ }
+
+ public @Nullable String getFsUuid() {
+ return fsUuid;
+ }
+
+ public @Nullable String getNickname() {
+ return nickname;
+ }
+
public @Nullable String getDescription() {
if (ID_PRIVATE_INTERNAL.equals(id)) {
return Resources.getSystem().getString(com.android.internal.R.string.storage_internal);
@@ -159,6 +194,14 @@ public class VolumeInfo implements Parcelable {
return (flags & FLAG_VISIBLE) != 0;
}
+ public boolean isInited() {
+ return (userFlags & USER_FLAG_INITED) != 0;
+ }
+
+ public boolean isSnoozed() {
+ return (userFlags & USER_FLAG_SNOOZED) != 0;
+ }
+
public boolean isVisibleToUser(int userId) {
if (type == TYPE_PUBLIC && userId == this.userId) {
return isVisible();
@@ -169,6 +212,10 @@ public class VolumeInfo implements Parcelable {
}
}
+ public File getPath() {
+ return new File(path);
+ }
+
public File getPathForUser(int userId) {
if (path == null) {
return null;
@@ -228,6 +275,34 @@ public class VolumeInfo implements Parcelable {
fsUuid, envState);
}
+ // TODO: avoid this layering violation
+ private static final String DOCUMENT_AUTHORITY = "com.android.externalstorage.documents";
+ private static final String DOCUMENT_ROOT_PRIMARY_EMULATED = "primary";
+
+ /**
+ * Build an intent to browse the contents of this volume. Only valid for
+ * {@link #TYPE_EMULATED} or {@link #TYPE_PUBLIC}.
+ */
+ public Intent buildBrowseIntent() {
+ final Uri uri;
+ if (type == VolumeInfo.TYPE_PUBLIC) {
+ uri = DocumentsContract.buildRootUri(DOCUMENT_AUTHORITY, fsUuid);
+ } else if (VolumeInfo.ID_EMULATED_INTERNAL.equals(id)) {
+ uri = DocumentsContract.buildRootUri(DOCUMENT_AUTHORITY,
+ DOCUMENT_ROOT_PRIMARY_EMULATED);
+ } else if (type == VolumeInfo.TYPE_EMULATED) {
+ // TODO: build intent once supported
+ uri = null;
+ } else {
+ throw new IllegalArgumentException();
+ }
+
+ final Intent intent = new Intent(DocumentsContract.ACTION_BROWSE_DOCUMENT_ROOT);
+ intent.addCategory(Intent.CATEGORY_DEFAULT);
+ intent.setData(uri);
+ return intent;
+ }
+
@Override
public String toString() {
final CharArrayWriter writer = new CharArrayWriter();
@@ -250,6 +325,9 @@ public class VolumeInfo implements Parcelable {
pw.println();
pw.printPair("path", path);
pw.printPair("mtpIndex", mtpIndex);
+ pw.printPair("diskId", diskId);
+ pw.printPair("nickname", nickname);
+ pw.printPair("userFlags", DebugUtils.flagsToString(getClass(), "USER_FLAG_", userFlags));
pw.decreaseIndent();
pw.println();
}
@@ -295,6 +373,8 @@ public class VolumeInfo implements Parcelable {
parcel.writeString(fsLabel);
parcel.writeString(path);
parcel.writeInt(mtpIndex);
+ parcel.writeString(diskId);
parcel.writeString(nickname);
+ parcel.writeInt(userFlags);
}
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 9edf6ad..f640f0d 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -2766,8 +2766,6 @@ public final class Settings {
* It was about AudioManager's setting and thus affected all the applications which
* relied on the setting, while this is purely about the vibration setting for incoming
* calls.
- *
- * @hide
*/
public static final String VIBRATE_WHEN_RINGING = "vibrate_when_ringing";
@@ -2788,7 +2786,6 @@ public final class Settings {
* DTMF tone type played by the dialer when dialing.
* 0 = Normal
* 1 = Long
- * @hide
*/
public static final String DTMF_TONE_TYPE_WHEN_DIALING = "dtmf_tone_type";
@@ -7057,7 +7054,6 @@ public final class Settings {
/**
* Setting to 1 will hide carrier network settings.
* Default is 0.
- * @hide
*/
public static final String HIDE_CARRIER_NETWORK_SETTINGS =
"hide_carrier_network_settings";
@@ -7717,6 +7713,13 @@ public final class Settings {
*/
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/gatekeeper/IGateKeeperService.aidl b/core/java/android/service/gatekeeper/IGateKeeperService.aidl
new file mode 100644
index 0000000..2f3e296
--- /dev/null
+++ b/core/java/android/service/gatekeeper/IGateKeeperService.aidl
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.service.gatekeeper;
+
+/**
+ * Interface for communication with GateKeeper, the
+ * secure password storage daemon.
+ *
+ * This must be kept manually in sync with system/core/gatekeeperd
+ * until AIDL can generate both C++ and Java bindings.
+ *
+ * @hide
+ */
+interface IGateKeeperService {
+ /**
+ * Enrolls a password, returning the handle to the enrollment to be stored locally.
+ * @param uid The Android user ID associated to this enrollment
+ * @param currentPasswordHandle The previously enrolled handle, or null if none
+ * @param currentPassword The previously enrolled plaintext password, or null if none.
+ * If provided, must verify against the currentPasswordHandle.
+ * @param desiredPassword The new desired password, for which a handle will be returned
+ * upon success.
+ * @return the handle corresponding to desiredPassword, or null
+ */
+ byte[] enroll(int uid, in byte[] currentPasswordHandle, in byte[] currentPassword,
+ in byte[] desiredPassword);
+
+ /**
+ * Verifies an enrolled handle against a provided, plaintext blob.
+ * @param uid The Android user ID associated to this enrollment
+ * @param enrolledPasswordHandle The handle against which the provided password will be
+ * verified.
+ * @param The plaintext blob to verify against enrolledPassword.
+ * @return True if the authentication was successful
+ */
+ boolean verify(int uid, in byte[] enrolledPasswordHandle,
+ in byte[] providedPassword);
+ /**
+ * Verifies an enrolled handle against a provided, plaintext blob.
+ * @param uid The Android user ID associated to this enrollment
+ * @param challenge a challenge to authenticate agaisnt the device credential. If successful
+ * authentication occurs, this value will be written to the returned
+ * authentication attestation.
+ * @param enrolledPasswordHandle The handle against which the provided password will be
+ * verified.
+ * @param The plaintext blob to verify against enrolledPassword.
+ * @return an opaque attestation of authentication on success, or null.
+ */
+ byte[] verifyChallenge(int uid, long challenge, in byte[] enrolledPasswordHandle,
+ in byte[] providedPassword);
+}
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.AssistStructure;
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 @@ import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
* 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 @@ public abstract class VoiceInteractionSession implements KeyEvent.Callback {
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 @@ package android.service.voice;
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 @@ public abstract class VoiceInteractionSessionService extends Service {
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/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index 2bcb352..7828851 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -811,7 +811,7 @@ public class StaticLayout extends Layout {
float sum = 0;
int i;
- for (i = len; i >= 0; i--) {
+ for (i = len; i > 0; i--) {
float w = widths[i - 1 + lineStart - widthStart];
if (w + sum + ellipsisWidth > avail) {
diff --git a/core/java/android/view/PhoneWindow.java b/core/java/android/view/PhoneWindow.java
index 5af2832..794c8e7 100644
--- a/core/java/android/view/PhoneWindow.java
+++ b/core/java/android/view/PhoneWindow.java
@@ -4257,7 +4257,13 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
if (deviceId != 0) {
searchEvent = new SearchEvent(InputDevice.getDevice(deviceId));
}
- result = cb.onSearchRequested(searchEvent);
+ try {
+ result = cb.onSearchRequested(searchEvent);
+ } catch (AbstractMethodError e) {
+ Log.e(TAG, "WindowCallback " + cb.getClass().getName() + " does not implement"
+ + " method onSearchRequested(SearchEvent); fa", e);
+ result = cb.onSearchRequested();
+ }
}
if (!result && (getContext().getResources().getConfiguration().uiMode
& Configuration.UI_MODE_TYPE_MASK) == Configuration.UI_MODE_TYPE_TELEVISION) {
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index 87d5d9a..5017a38 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -360,7 +360,7 @@ public class ThreadedRenderer extends HardwareRenderer {
@Override
boolean copyLayerInto(final HardwareLayer layer, final Bitmap bitmap) {
return nCopyLayerInto(mNativeProxy,
- layer.getDeferredLayerUpdater(), bitmap.getSkBitmap());
+ layer.getDeferredLayerUpdater(), bitmap);
}
@Override
@@ -531,7 +531,7 @@ public class ThreadedRenderer extends HardwareRenderer {
private static native long nCreateTextureLayer(long nativeProxy);
private static native void nBuildLayer(long nativeProxy, long node);
- private static native boolean nCopyLayerInto(long nativeProxy, long layer, long bitmap);
+ private static native boolean nCopyLayerInto(long nativeProxy, long layer, Bitmap bitmap);
private static native void nPushLayerUpdate(long nativeProxy, long layer);
private static native void nCancelLayerUpdate(long nativeProxy, long layer);
private static native void nDetachSurfaceTexture(long nativeProxy, long layer);
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 3fa8c81..25fa349 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -1795,6 +1795,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* 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 class View implements Drawable.Callback, KeyEvent.Callback,
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 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
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
};
/**
@@ -15187,27 +15205,33 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
/**
* This method is called by ViewGroup.drawChild() to have each child view draw itself.
- * This draw() method is an implementation detail and is not intended to be overridden or
- * to be called from anywhere else other than ViewGroup.drawChild().
+ *
+ * This is where the View specializes rendering behavior based on layer type,
+ * and hardware acceleration.
*/
boolean draw(Canvas canvas, ViewGroup parent, long drawingTime) {
- boolean usingRenderNodeProperties = mAttachInfo != null && mAttachInfo.mHardwareAccelerated;
+ final boolean hardwareAcceleratedCanvas = canvas.isHardwareAccelerated();
+ /* If an attached view draws to a HW canvas, it may use its RenderNode + DisplayList.
+ *
+ * If a view is dettached, its DisplayList shouldn't exist. If the canvas isn't
+ * HW accelerated, it can't handle drawing RenderNodes.
+ */
+ boolean drawingWithRenderNode = mAttachInfo != null
+ && mAttachInfo.mHardwareAccelerated
+ && hardwareAcceleratedCanvas;
+
boolean more = false;
final boolean childHasIdentityMatrix = hasIdentityMatrix();
- final int flags = parent.mGroupFlags;
+ final int parentFlags = parent.mGroupFlags;
- if ((flags & ViewGroup.FLAG_CLEAR_TRANSFORMATION) == ViewGroup.FLAG_CLEAR_TRANSFORMATION) {
+ if ((parentFlags & ViewGroup.FLAG_CLEAR_TRANSFORMATION) != 0) {
parent.getChildTransformation().clear();
parent.mGroupFlags &= ~ViewGroup.FLAG_CLEAR_TRANSFORMATION;
}
Transformation transformToApply = null;
boolean concatMatrix = false;
-
- boolean scalingRequired = mAttachInfo != null && mAttachInfo.mScalingRequired;
- int layerType = getLayerType();
- final boolean hardwareAccelerated = canvas.isHardwareAccelerated();
-
+ final boolean scalingRequired = mAttachInfo != null && mAttachInfo.mScalingRequired;
final Animation a = getAnimation();
if (a != null) {
more = applyLegacyAnimation(parent, drawingTime, a, scalingRequired);
@@ -15222,8 +15246,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
mRenderNode.setAnimationMatrix(null);
mPrivateFlags3 &= ~PFLAG3_VIEW_IS_ANIMATING_TRANSFORM;
}
- if (!usingRenderNodeProperties &&
- (flags & ViewGroup.FLAG_SUPPORT_STATIC_TRANSFORMATIONS) != 0) {
+ if (!drawingWithRenderNode
+ && (parentFlags & ViewGroup.FLAG_SUPPORT_STATIC_TRANSFORMATIONS) != 0) {
final Transformation t = parent.getChildTransformation();
final boolean hasTransform = parent.getChildStaticTransformation(this, t);
if (hasTransform) {
@@ -15241,7 +15265,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
mPrivateFlags |= PFLAG_DRAWN;
if (!concatMatrix &&
- (flags & (ViewGroup.FLAG_SUPPORT_STATIC_TRANSFORMATIONS |
+ (parentFlags & (ViewGroup.FLAG_SUPPORT_STATIC_TRANSFORMATIONS |
ViewGroup.FLAG_CLIP_CHILDREN)) == ViewGroup.FLAG_CLIP_CHILDREN &&
canvas.quickReject(mLeft, mTop, mRight, mBottom, Canvas.EdgeType.BW) &&
(mPrivateFlags & PFLAG_DRAW_ANIMATION) == 0) {
@@ -15250,81 +15274,61 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
mPrivateFlags2 &= ~PFLAG2_VIEW_QUICK_REJECTED;
- if (hardwareAccelerated) {
+ if (hardwareAcceleratedCanvas) {
// Clear INVALIDATED flag to allow invalidation to occur during rendering, but
// retain the flag's value temporarily in the mRecreateDisplayList flag
- mRecreateDisplayList = (mPrivateFlags & PFLAG_INVALIDATED) == PFLAG_INVALIDATED;
+ mRecreateDisplayList = (mPrivateFlags & PFLAG_INVALIDATED) != 0;
mPrivateFlags &= ~PFLAG_INVALIDATED;
}
RenderNode renderNode = null;
Bitmap cache = null;
- boolean hasDisplayList = false;
- if (!hardwareAccelerated) {
- if (layerType != LAYER_TYPE_NONE) {
- layerType = LAYER_TYPE_SOFTWARE;
- buildDrawingCache(true);
- }
+ int layerType = getLayerType();
+ if (layerType == LAYER_TYPE_SOFTWARE
+ || (!drawingWithRenderNode && layerType != LAYER_TYPE_NONE)) {
+ layerType = LAYER_TYPE_SOFTWARE;
+ buildDrawingCache(true);
cache = getDrawingCache(true);
- } else {
- switch (layerType) {
- case LAYER_TYPE_SOFTWARE:
- if (usingRenderNodeProperties) {
- hasDisplayList = canHaveDisplayList();
- } else {
- buildDrawingCache(true);
- cache = getDrawingCache(true);
- }
- break;
- case LAYER_TYPE_HARDWARE:
- if (usingRenderNodeProperties) {
- hasDisplayList = canHaveDisplayList();
- }
- break;
- case LAYER_TYPE_NONE:
- // Delay getting the display list until animation-driven alpha values are
- // set up and possibly passed on to the view
- hasDisplayList = canHaveDisplayList();
- break;
- }
}
- usingRenderNodeProperties &= hasDisplayList;
- if (usingRenderNodeProperties) {
+
+ if (drawingWithRenderNode) {
+ // Delay getting the display list until animation-driven alpha values are
+ // set up and possibly passed on to the view
renderNode = getDisplayList();
if (!renderNode.isValid()) {
// Uncommon, but possible. If a view is removed from the hierarchy during the call
// to getDisplayList(), the display list will be marked invalid and we should not
// try to use it again.
renderNode = null;
- hasDisplayList = false;
- usingRenderNodeProperties = false;
+ drawingWithRenderNode = false;
}
}
int sx = 0;
int sy = 0;
- if (!hasDisplayList) {
+ if (!drawingWithRenderNode) {
computeScroll();
sx = mScrollX;
sy = mScrollY;
}
- final boolean hasNoCache = cache == null || hasDisplayList;
- final boolean offsetForScroll = cache == null && !hasDisplayList &&
- layerType != LAYER_TYPE_HARDWARE;
+ final boolean hasNoCache = cache == null || drawingWithRenderNode;
+ final boolean offsetForScroll = cache == null
+ && !drawingWithRenderNode
+ && layerType != LAYER_TYPE_HARDWARE;
int restoreTo = -1;
- if (!usingRenderNodeProperties || transformToApply != null) {
+ if (!drawingWithRenderNode || transformToApply != null) {
restoreTo = canvas.save();
}
if (offsetForScroll) {
canvas.translate(mLeft - sx, mTop - sy);
} else {
- if (!usingRenderNodeProperties) {
+ if (!drawingWithRenderNode) {
canvas.translate(mLeft, mTop);
}
if (scalingRequired) {
- if (usingRenderNodeProperties) {
+ if (drawingWithRenderNode) {
// TODO: Might not need this if we put everything inside the DL
restoreTo = canvas.save();
}
@@ -15334,9 +15338,11 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
}
- float alpha = usingRenderNodeProperties ? 1 : (getAlpha() * getTransitionAlpha());
- if (transformToApply != null || alpha < 1 || !hasIdentityMatrix() ||
- (mPrivateFlags3 & PFLAG3_VIEW_IS_ANIMATING_ALPHA) == PFLAG3_VIEW_IS_ANIMATING_ALPHA) {
+ float alpha = drawingWithRenderNode ? 1 : (getAlpha() * getTransitionAlpha());
+ if (transformToApply != null
+ || alpha < 1
+ || !hasIdentityMatrix()
+ || (mPrivateFlags3 & PFLAG3_VIEW_IS_ANIMATING_ALPHA) != 0) {
if (transformToApply != null || !childHasIdentityMatrix) {
int transX = 0;
int transY = 0;
@@ -15348,7 +15354,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
if (transformToApply != null) {
if (concatMatrix) {
- if (usingRenderNodeProperties) {
+ if (drawingWithRenderNode) {
renderNode.setAnimationMatrix(transformToApply.getMatrix());
} else {
// Undo the scroll translation, apply the transformation matrix,
@@ -15367,7 +15373,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
}
- if (!childHasIdentityMatrix && !usingRenderNodeProperties) {
+ if (!childHasIdentityMatrix && !drawingWithRenderNode) {
canvas.translate(-transX, -transY);
canvas.concat(getMatrix());
canvas.translate(transX, transY);
@@ -15375,8 +15381,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
// Deal with alpha if it is or used to be <1
- if (alpha < 1 ||
- (mPrivateFlags3 & PFLAG3_VIEW_IS_ANIMATING_ALPHA) == PFLAG3_VIEW_IS_ANIMATING_ALPHA) {
+ if (alpha < 1 || (mPrivateFlags3 & PFLAG3_VIEW_IS_ANIMATING_ALPHA) != 0) {
if (alpha < 1) {
mPrivateFlags3 |= PFLAG3_VIEW_IS_ANIMATING_ALPHA;
} else {
@@ -15387,17 +15392,14 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
final int multipliedAlpha = (int) (255 * alpha);
if (!onSetAlpha(multipliedAlpha)) {
int layerFlags = Canvas.HAS_ALPHA_LAYER_SAVE_FLAG;
- if ((flags & ViewGroup.FLAG_CLIP_CHILDREN) != 0 ||
- layerType != LAYER_TYPE_NONE) {
+ if ((parentFlags & ViewGroup.FLAG_CLIP_CHILDREN) != 0
+ || layerType != LAYER_TYPE_NONE) {
layerFlags |= Canvas.CLIP_TO_LAYER_SAVE_FLAG;
}
- if (usingRenderNodeProperties) {
+ if (drawingWithRenderNode) {
renderNode.setAlpha(alpha * getAlpha() * getTransitionAlpha());
- } else if (layerType == LAYER_TYPE_NONE) {
- final int scrollX = hasDisplayList ? 0 : sx;
- final int scrollY = hasDisplayList ? 0 : sy;
- canvas.saveLayerAlpha(scrollX, scrollY,
- scrollX + (mRight - mLeft), scrollY + (mBottom - mTop),
+ } else if (layerType == LAYER_TYPE_NONE) {
+ canvas.saveLayerAlpha(sx, sy, sx + getWidth(), sy + getHeight(),
multipliedAlpha, layerFlags);
}
} else {
@@ -15411,15 +15413,14 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
mPrivateFlags &= ~PFLAG_ALPHA_SET;
}
- if (!usingRenderNodeProperties) {
+ if (!drawingWithRenderNode) {
// apply clips directly, since RenderNode won't do it for this draw
- if ((flags & ViewGroup.FLAG_CLIP_CHILDREN) == ViewGroup.FLAG_CLIP_CHILDREN
- && cache == null) {
+ if ((parentFlags & ViewGroup.FLAG_CLIP_CHILDREN) != 0 && cache == null) {
if (offsetForScroll) {
- canvas.clipRect(sx, sy, sx + (mRight - mLeft), sy + (mBottom - mTop));
+ canvas.clipRect(sx, sy, sx + getWidth(), sy + getHeight());
} else {
if (!scalingRequired || cache == null) {
- canvas.clipRect(0, 0, mRight - mLeft, mBottom - mTop);
+ canvas.clipRect(0, 0, getWidth(), getHeight());
} else {
canvas.clipRect(0, 0, cache.getWidth(), cache.getHeight());
}
@@ -15432,22 +15433,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
}
-
-
- if (!usingRenderNodeProperties && hasDisplayList) {
- renderNode = getDisplayList();
- if (!renderNode.isValid()) {
- // Uncommon, but possible. If a view is removed from the hierarchy during the call
- // to getDisplayList(), the display list will be marked invalid and we should not
- // try to use it again.
- renderNode = null;
- hasDisplayList = false;
- }
- }
-
if (hasNoCache) {
boolean layerRendered = false;
- if (layerType == LAYER_TYPE_HARDWARE && !usingRenderNodeProperties) {
+ if (layerType == LAYER_TYPE_HARDWARE && !drawingWithRenderNode) {
final HardwareLayer layer = getHardwareLayer();
if (layer != null && layer.isValid()) {
int restoreAlpha = mLayerPaint.getAlpha();
@@ -15456,16 +15444,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
mLayerPaint.setAlpha(restoreAlpha);
layerRendered = true;
} else {
- final int scrollX = hasDisplayList ? 0 : sx;
- final int scrollY = hasDisplayList ? 0 : sy;
- canvas.saveLayer(scrollX, scrollY,
- scrollX + mRight - mLeft, scrollY + mBottom - mTop, mLayerPaint,
- Canvas.HAS_ALPHA_LAYER_SAVE_FLAG | Canvas.CLIP_TO_LAYER_SAVE_FLAG);
+ canvas.saveLayer(sx, sy, sx + getWidth(), sy + getHeight(), mLayerPaint);
}
}
if (!layerRendered) {
- if (!hasDisplayList) {
+ if (!drawingWithRenderNode) {
// Fast path for layouts with no backgrounds
if ((mPrivateFlags & PFLAG_SKIP_DRAW) == PFLAG_SKIP_DRAW) {
mPrivateFlags &= ~PFLAG_DIRTY_MASK;
@@ -15475,7 +15459,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
} else {
mPrivateFlags &= ~PFLAG_DIRTY_MASK;
- ((DisplayListCanvas) canvas).drawRenderNode(renderNode, flags);
+ ((DisplayListCanvas) canvas).drawRenderNode(renderNode, parentFlags);
}
}
} else if (cache != null) {
@@ -15504,13 +15488,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
if (a != null && !more) {
- if (!hardwareAccelerated && !a.getFillAfter()) {
+ if (!hardwareAcceleratedCanvas && !a.getFillAfter()) {
onSetAlpha(255);
}
parent.finishAnimatingView(this, a);
}
- if (more && hardwareAccelerated) {
+ if (more && hardwareAcceleratedCanvas) {
if (a.hasAlpha() && (mPrivateFlags & PFLAG_ALPHA_SET) == PFLAG_ALPHA_SET) {
// alpha animations should cause the child to recreate its display list
invalidate(true);
@@ -19636,11 +19620,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* @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 +19638,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
@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 +19652,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
* @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 +19688,13 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
*
* @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 +19704,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
@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 +19765,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
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 +19780,8 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
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/view/Window.java b/core/java/android/view/Window.java
index 9d0d5ff..49a72ce 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -595,6 +595,8 @@ public abstract class Window {
title="Panel";
} else if (wp.type == WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL) {
title="SubPanel";
+ } else if (wp.type == WindowManager.LayoutParams.TYPE_APPLICATION_ABOVE_SUB_PANEL) {
+ title="AboveSubPanel";
} else if (wp.type == WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG) {
title="AtchDlg";
} else {
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index 54d78f3..e983910 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -164,13 +164,14 @@ public interface WindowManager extends ViewManager {
* be used by applications, and a special permission is required
* to use them.
* </ul>
- *
+ *
* @see #TYPE_BASE_APPLICATION
* @see #TYPE_APPLICATION
* @see #TYPE_APPLICATION_STARTING
* @see #TYPE_APPLICATION_PANEL
* @see #TYPE_APPLICATION_MEDIA
* @see #TYPE_APPLICATION_SUB_PANEL
+ * @see #TYPE_APPLICATION_ABOVE_SUB_PANEL
* @see #TYPE_APPLICATION_ATTACHED_DIALOG
* @see #TYPE_STATUS_BAR
* @see #TYPE_SEARCH_BAR
@@ -193,6 +194,7 @@ public interface WindowManager extends ViewManager {
@ViewDebug.IntToString(from = TYPE_APPLICATION_PANEL, to = "TYPE_APPLICATION_PANEL"),
@ViewDebug.IntToString(from = TYPE_APPLICATION_MEDIA, to = "TYPE_APPLICATION_MEDIA"),
@ViewDebug.IntToString(from = TYPE_APPLICATION_SUB_PANEL, to = "TYPE_APPLICATION_SUB_PANEL"),
+ @ViewDebug.IntToString(from = TYPE_APPLICATION_ABOVE_SUB_PANEL, to = "TYPE_APPLICATION_ABOVE_SUB_PANEL"),
@ViewDebug.IntToString(from = TYPE_APPLICATION_ATTACHED_DIALOG, to = "TYPE_APPLICATION_ATTACHED_DIALOG"),
@ViewDebug.IntToString(from = TYPE_APPLICATION_MEDIA_OVERLAY, to = "TYPE_APPLICATION_MEDIA_OVERLAY"),
@ViewDebug.IntToString(from = TYPE_STATUS_BAR, to = "TYPE_STATUS_BAR"),
@@ -260,40 +262,40 @@ public interface WindowManager extends ViewManager {
* End of types of application windows.
*/
public static final int LAST_APPLICATION_WINDOW = 99;
-
+
/**
* Start of types of sub-windows. The {@link #token} of these windows
* must be set to the window they are attached to. These types of
* windows are kept next to their attached window in Z-order, and their
* coordinate space is relative to their attached window.
*/
- public static final int FIRST_SUB_WINDOW = 1000;
-
+ public static final int FIRST_SUB_WINDOW = 1000;
+
/**
* Window type: a panel on top of an application window. These windows
* appear on top of their attached window.
*/
- public static final int TYPE_APPLICATION_PANEL = FIRST_SUB_WINDOW;
-
+ public static final int TYPE_APPLICATION_PANEL = FIRST_SUB_WINDOW;
+
/**
* Window type: window for showing media (such as video). These windows
* are displayed behind their attached window.
*/
- public static final int TYPE_APPLICATION_MEDIA = FIRST_SUB_WINDOW+1;
-
+ public static final int TYPE_APPLICATION_MEDIA = FIRST_SUB_WINDOW + 1;
+
/**
* Window type: a sub-panel on top of an application window. These
* windows are displayed on top their attached window and any
* {@link #TYPE_APPLICATION_PANEL} panels.
*/
- public static final int TYPE_APPLICATION_SUB_PANEL = FIRST_SUB_WINDOW+2;
+ public static final int TYPE_APPLICATION_SUB_PANEL = FIRST_SUB_WINDOW + 2;
/** Window type: like {@link #TYPE_APPLICATION_PANEL}, but layout
* of the window happens as that of a top-level window, <em>not</em>
* as a child of its container.
*/
- public static final int TYPE_APPLICATION_ATTACHED_DIALOG = FIRST_SUB_WINDOW+3;
-
+ public static final int TYPE_APPLICATION_ATTACHED_DIALOG = FIRST_SUB_WINDOW + 3;
+
/**
* Window type: window for showing overlays on top of media windows.
* These windows are displayed between TYPE_APPLICATION_MEDIA and the
@@ -301,19 +303,26 @@ public interface WindowManager extends ViewManager {
* is a big ugly hack so:
* @hide
*/
- public static final int TYPE_APPLICATION_MEDIA_OVERLAY = FIRST_SUB_WINDOW+4;
-
+ public static final int TYPE_APPLICATION_MEDIA_OVERLAY = FIRST_SUB_WINDOW + 4;
+
+ /**
+ * Window type: a above sub-panel on top of an application window and it's
+ * sub-panel windows. These windows are displayed on top of their attached window
+ * and any {@link #TYPE_APPLICATION_SUB_PANEL} panels.
+ */
+ public static final int TYPE_APPLICATION_ABOVE_SUB_PANEL = FIRST_SUB_WINDOW + 5;
+
/**
* End of types of sub-windows.
*/
- public static final int LAST_SUB_WINDOW = 1999;
-
+ public static final int LAST_SUB_WINDOW = 1999;
+
/**
* Start of system-specific window types. These are not normally
* created by applications.
*/
public static final int FIRST_SYSTEM_WINDOW = 2000;
-
+
/**
* Window type: the status bar. There can be only one status bar
* window; it is placed at the top of the screen, and all other
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 @@ public class Editor {
// 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 @@ public class Editor {
}
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 @@ public class Editor {
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 @@ public class Editor {
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 @@ public class Editor {
}
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 @@ public class Editor {
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 @@ public class Editor {
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/PopupWindow.java b/core/java/android/widget/PopupWindow.java
index 8792323..c5b5c84 100644
--- a/core/java/android/widget/PopupWindow.java
+++ b/core/java/android/widget/PopupWindow.java
@@ -860,19 +860,22 @@ public class PopupWindow {
}
/**
- * Set the layout type for this window. Should be one of the TYPE constants defined in
- * {@link WindowManager.LayoutParams}.
+ * Set the layout type for this window. This value will be passed through to
+ * {@link WindowManager.LayoutParams#type} therefore the value should match any value
+ * {@link WindowManager.LayoutParams#type} accepts.
*
* @param layoutType Layout type for this window.
- * @hide
+ *
+ * @see WindowManager.LayoutParams#type
*/
public void setWindowLayoutType(int layoutType) {
mWindowLayoutType = layoutType;
}
/**
- * @return The layout type for this window.
- * @hide
+ * Returns the layout type for this window.
+ *
+ * @see #setWindowLayoutType(int)
*/
public int getWindowLayoutType() {
return mWindowLayoutType;
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 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
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;
}
}