Merge "Fix the empty reason field in connectivity change intent." into mnc-dev
diff --git a/Android.mk b/Android.mk
index d6dac53..4d8a37c 100644
--- a/Android.mk
+++ b/Android.mk
@@ -555,8 +555,6 @@
frameworks/base/core/java/android/service/chooser/ChooserTarget.aidl \
frameworks/base/core/java/android/speech/tts/Voice.aidl \
frameworks/base/core/java/android/app/usage/UsageEvents.aidl \
- frameworks/base/core/java/android/app/AssistStructure.aidl \
- frameworks/base/core/java/android/app/AssistContent.aidl \
frameworks/base/core/java/android/app/Notification.aidl \
frameworks/base/core/java/android/app/NotificationManager.aidl \
frameworks/base/core/java/android/app/WallpaperInfo.aidl \
@@ -566,6 +564,8 @@
frameworks/base/core/java/android/app/AlarmManager.aidl \
frameworks/base/core/java/android/app/SearchableInfo.aidl \
frameworks/base/core/java/android/app/VoiceInteractor.aidl \
+ frameworks/base/core/java/android/app/assist/AssistContent.aidl \
+ frameworks/base/core/java/android/app/assist/AssistStructure.aidl \
frameworks/base/core/java/android/app/job/JobParameters.aidl \
frameworks/base/core/java/android/app/job/JobInfo.aidl \
frameworks/base/core/java/android/appwidget/AppWidgetProviderInfo.aidl \
diff --git a/api/current.txt b/api/current.txt
index 3c878c8..c9a8efb 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -3445,7 +3445,7 @@
method public void onPrepareNavigateUpTaskStack(android.app.TaskStackBuilder);
method public boolean onPrepareOptionsMenu(android.view.Menu);
method public boolean onPreparePanel(int, android.view.View, android.view.Menu);
- method public void onProvideAssistContent(android.app.AssistContent);
+ method public void onProvideAssistContent(android.app.assist.AssistContent);
method public void onProvideAssistData(android.os.Bundle);
method public void onRequestPermissionsResult(int, java.lang.String[], int[]);
method protected void onRestart();
@@ -3996,26 +3996,19 @@
field public java.lang.String serviceDetails;
}
- public class AssistContent implements android.os.Parcelable {
+ public deprecated class AssistContent {
ctor public AssistContent();
- method public int describeContents();
method public android.content.ClipData getClipData();
- method public android.content.Intent getIntent();
method public android.net.Uri getWebUri();
method public void setClipData(android.content.ClipData);
method public void setIntent(android.content.Intent);
method public void setWebUri(android.net.Uri);
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator<android.app.AssistContent> CREATOR;
}
- public final class AssistStructure implements android.os.Parcelable {
- method public int describeContents();
+ public deprecated class AssistStructure {
+ ctor public AssistStructure();
method public android.content.ComponentName getActivityComponent();
- method public android.app.AssistStructure.WindowNode getWindowNodeAt(int);
method public int getWindowNodeCount();
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator<android.app.AssistStructure> CREATOR;
}
public static class AssistStructure.ViewNode {
@@ -4384,6 +4377,7 @@
method public void setSharedElementReturnTransition(android.transition.Transition);
method public void setTargetFragment(android.app.Fragment, int);
method public void setUserVisibleHint(boolean);
+ method public boolean shouldShowRequestPermissionRationale(java.lang.String);
method public void startActivity(android.content.Intent);
method public void startActivity(android.content.Intent, android.os.Bundle);
method public void startActivityForResult(android.content.Intent, int);
@@ -5899,6 +5893,26 @@
}
+package android.app.assist {
+
+ public final class AssistContent extends android.app.AssistContent implements android.os.Parcelable {
+ ctor public AssistContent(android.os.Parcel);
+ method public int describeContents();
+ method public android.content.Intent getIntent();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.app.assist.AssistContent> CREATOR;
+ }
+
+ public final class AssistStructure extends android.app.AssistStructure implements android.os.Parcelable {
+ ctor public AssistStructure();
+ method public int describeContents();
+ method public android.app.AssistStructure.WindowNode getWindowNodeAt(int);
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.app.assist.AssistStructure> CREATOR;
+ }
+
+}
+
package android.app.backup {
public abstract class BackupAgent extends android.content.ContextWrapper {
@@ -13191,6 +13205,7 @@
method public abstract void close();
method public abstract android.hardware.camera2.CameraDevice getDevice();
method public abstract android.view.Surface getInputSurface();
+ method public abstract boolean isConstrainedHighSpeed();
method public abstract boolean isReprocessable();
method public abstract void prepare(android.view.Surface) throws android.hardware.camera2.CameraAccessException;
method public abstract int setRepeatingBurst(java.util.List<android.hardware.camera2.CaptureRequest>, android.hardware.camera2.CameraCaptureSession.CaptureCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
@@ -13311,6 +13326,8 @@
method public abstract void close();
method public abstract android.hardware.camera2.CaptureRequest.Builder createCaptureRequest(int) throws android.hardware.camera2.CameraAccessException;
method public abstract void createCaptureSession(java.util.List<android.view.Surface>, android.hardware.camera2.CameraCaptureSession.StateCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
+ method public abstract void createConstrainedHighSpeedCaptureSession(java.util.List<android.view.Surface>, android.hardware.camera2.CameraCaptureSession.StateCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
+ method public abstract java.util.List<android.hardware.camera2.CaptureRequest> createConstrainedHighSpeedRequestList(android.hardware.camera2.CaptureRequest) throws android.hardware.camera2.CameraAccessException;
method public abstract android.hardware.camera2.CaptureRequest.Builder createReprocessCaptureRequest(android.hardware.camera2.TotalCaptureResult) throws android.hardware.camera2.CameraAccessException;
method public abstract void createReprocessableCaptureSession(android.hardware.camera2.params.InputConfiguration, java.util.List<android.view.Surface>, android.hardware.camera2.CameraCaptureSession.StateCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
method public abstract java.lang.String getId();
@@ -13441,7 +13458,7 @@
field public static final int CONTROL_SCENE_MODE_FACE_PRIORITY = 1; // 0x1
field public static final int CONTROL_SCENE_MODE_FIREWORKS = 12; // 0xc
field public static final int CONTROL_SCENE_MODE_HDR = 18; // 0x12
- field public static final int CONTROL_SCENE_MODE_HIGH_SPEED_VIDEO = 17; // 0x11
+ field public static final deprecated int CONTROL_SCENE_MODE_HIGH_SPEED_VIDEO = 17; // 0x11
field public static final int CONTROL_SCENE_MODE_LANDSCAPE = 4; // 0x4
field public static final int CONTROL_SCENE_MODE_NIGHT = 5; // 0x5
field public static final int CONTROL_SCENE_MODE_NIGHT_PORTRAIT = 6; // 0x6
@@ -13488,6 +13505,7 @@
field public static final int NOISE_REDUCTION_MODE_OFF = 0; // 0x0
field public static final int REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE = 0; // 0x0
field public static final int REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE = 6; // 0x6
+ field public static final int REQUEST_AVAILABLE_CAPABILITIES_CONSTRAINED_HIGH_SPEED_VIDEO = 9; // 0x9
field public static final int REQUEST_AVAILABLE_CAPABILITIES_DEPTH_OUTPUT = 8; // 0x8
field public static final int REQUEST_AVAILABLE_CAPABILITIES_MANUAL_POST_PROCESSING = 2; // 0x2
field public static final int REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR = 1; // 0x1
@@ -28930,7 +28948,7 @@
method public android.view.View onCreateContentView();
method public void onDestroy();
method public boolean[] onGetSupportedCommands(java.lang.String[]);
- method public void onHandleAssist(android.os.Bundle, android.app.AssistStructure, android.app.AssistContent);
+ method public void onHandleAssist(android.os.Bundle, android.app.assist.AssistStructure, android.app.assist.AssistContent);
method public void onHide();
method public boolean onKeyDown(int, android.view.KeyEvent);
method public boolean onKeyLongPress(int, android.view.KeyEvent);
@@ -32454,7 +32472,7 @@
method public android.text.StaticLayout.Builder setLineSpacing(float, float);
method public android.text.StaticLayout.Builder setMaxLines(int);
method public android.text.StaticLayout.Builder setText(java.lang.CharSequence);
- method public android.text.StaticLayout.Builder setTextDir(android.text.TextDirectionHeuristic);
+ method public android.text.StaticLayout.Builder setTextDirection(android.text.TextDirectionHeuristic);
}
public abstract interface TextDirectionHeuristic {
diff --git a/api/system-current.txt b/api/system-current.txt
index 65ca29a..2f26891 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -3531,7 +3531,7 @@
method public void onPrepareNavigateUpTaskStack(android.app.TaskStackBuilder);
method public boolean onPrepareOptionsMenu(android.view.Menu);
method public boolean onPreparePanel(int, android.view.View, android.view.Menu);
- method public void onProvideAssistContent(android.app.AssistContent);
+ method public void onProvideAssistContent(android.app.assist.AssistContent);
method public void onProvideAssistData(android.os.Bundle);
method public void onRequestPermissionsResult(int, java.lang.String[], int[]);
method protected void onRestart();
@@ -4092,26 +4092,19 @@
field public java.lang.String serviceDetails;
}
- public class AssistContent implements android.os.Parcelable {
+ public deprecated class AssistContent {
ctor public AssistContent();
- method public int describeContents();
method public android.content.ClipData getClipData();
- method public android.content.Intent getIntent();
method public android.net.Uri getWebUri();
method public void setClipData(android.content.ClipData);
method public void setIntent(android.content.Intent);
method public void setWebUri(android.net.Uri);
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator<android.app.AssistContent> CREATOR;
}
- public final class AssistStructure implements android.os.Parcelable {
- method public int describeContents();
+ public deprecated class AssistStructure {
+ ctor public AssistStructure();
method public android.content.ComponentName getActivityComponent();
- method public android.app.AssistStructure.WindowNode getWindowNodeAt(int);
method public int getWindowNodeCount();
- method public void writeToParcel(android.os.Parcel, int);
- field public static final android.os.Parcelable.Creator<android.app.AssistStructure> CREATOR;
}
public static class AssistStructure.ViewNode {
@@ -4480,6 +4473,7 @@
method public void setSharedElementReturnTransition(android.transition.Transition);
method public void setTargetFragment(android.app.Fragment, int);
method public void setUserVisibleHint(boolean);
+ method public boolean shouldShowRequestPermissionRationale(java.lang.String);
method public void startActivity(android.content.Intent);
method public void startActivity(android.content.Intent, android.os.Bundle);
method public void startActivityForResult(android.content.Intent, int);
@@ -6014,6 +6008,26 @@
}
+package android.app.assist {
+
+ public final class AssistContent extends android.app.AssistContent implements android.os.Parcelable {
+ ctor public AssistContent(android.os.Parcel);
+ method public int describeContents();
+ method public android.content.Intent getIntent();
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.app.assist.AssistContent> CREATOR;
+ }
+
+ public final class AssistStructure extends android.app.AssistStructure implements android.os.Parcelable {
+ ctor public AssistStructure();
+ method public int describeContents();
+ method public android.app.AssistStructure.WindowNode getWindowNodeAt(int);
+ method public void writeToParcel(android.os.Parcel, int);
+ field public static final android.os.Parcelable.Creator<android.app.assist.AssistStructure> CREATOR;
+ }
+
+}
+
package android.app.backup {
public abstract class BackupAgent extends android.content.ContextWrapper {
@@ -13509,6 +13523,7 @@
method public abstract void close();
method public abstract android.hardware.camera2.CameraDevice getDevice();
method public abstract android.view.Surface getInputSurface();
+ method public abstract boolean isConstrainedHighSpeed();
method public abstract boolean isReprocessable();
method public abstract void prepare(android.view.Surface) throws android.hardware.camera2.CameraAccessException;
method public abstract int setRepeatingBurst(java.util.List<android.hardware.camera2.CaptureRequest>, android.hardware.camera2.CameraCaptureSession.CaptureCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
@@ -13629,6 +13644,8 @@
method public abstract void close();
method public abstract android.hardware.camera2.CaptureRequest.Builder createCaptureRequest(int) throws android.hardware.camera2.CameraAccessException;
method public abstract void createCaptureSession(java.util.List<android.view.Surface>, android.hardware.camera2.CameraCaptureSession.StateCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
+ method public abstract void createConstrainedHighSpeedCaptureSession(java.util.List<android.view.Surface>, android.hardware.camera2.CameraCaptureSession.StateCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
+ method public abstract java.util.List<android.hardware.camera2.CaptureRequest> createConstrainedHighSpeedRequestList(android.hardware.camera2.CaptureRequest) throws android.hardware.camera2.CameraAccessException;
method public abstract android.hardware.camera2.CaptureRequest.Builder createReprocessCaptureRequest(android.hardware.camera2.TotalCaptureResult) throws android.hardware.camera2.CameraAccessException;
method public abstract void createReprocessableCaptureSession(android.hardware.camera2.params.InputConfiguration, java.util.List<android.view.Surface>, android.hardware.camera2.CameraCaptureSession.StateCallback, android.os.Handler) throws android.hardware.camera2.CameraAccessException;
method public abstract java.lang.String getId();
@@ -13759,7 +13776,7 @@
field public static final int CONTROL_SCENE_MODE_FACE_PRIORITY = 1; // 0x1
field public static final int CONTROL_SCENE_MODE_FIREWORKS = 12; // 0xc
field public static final int CONTROL_SCENE_MODE_HDR = 18; // 0x12
- field public static final int CONTROL_SCENE_MODE_HIGH_SPEED_VIDEO = 17; // 0x11
+ field public static final deprecated int CONTROL_SCENE_MODE_HIGH_SPEED_VIDEO = 17; // 0x11
field public static final int CONTROL_SCENE_MODE_LANDSCAPE = 4; // 0x4
field public static final int CONTROL_SCENE_MODE_NIGHT = 5; // 0x5
field public static final int CONTROL_SCENE_MODE_NIGHT_PORTRAIT = 6; // 0x6
@@ -13806,6 +13823,7 @@
field public static final int NOISE_REDUCTION_MODE_OFF = 0; // 0x0
field public static final int REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE = 0; // 0x0
field public static final int REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE = 6; // 0x6
+ field public static final int REQUEST_AVAILABLE_CAPABILITIES_CONSTRAINED_HIGH_SPEED_VIDEO = 9; // 0x9
field public static final int REQUEST_AVAILABLE_CAPABILITIES_DEPTH_OUTPUT = 8; // 0x8
field public static final int REQUEST_AVAILABLE_CAPABILITIES_MANUAL_POST_PROCESSING = 2; // 0x2
field public static final int REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR = 1; // 0x1
@@ -31063,7 +31081,7 @@
method public android.view.View onCreateContentView();
method public void onDestroy();
method public boolean[] onGetSupportedCommands(java.lang.String[]);
- method public void onHandleAssist(android.os.Bundle, android.app.AssistStructure, android.app.AssistContent);
+ method public void onHandleAssist(android.os.Bundle, android.app.assist.AssistStructure, android.app.assist.AssistContent);
method public void onHide();
method public boolean onKeyDown(int, android.view.KeyEvent);
method public boolean onKeyLongPress(int, android.view.KeyEvent);
@@ -34719,7 +34737,7 @@
method public android.text.StaticLayout.Builder setLineSpacing(float, float);
method public android.text.StaticLayout.Builder setMaxLines(int);
method public android.text.StaticLayout.Builder setText(java.lang.CharSequence);
- method public android.text.StaticLayout.Builder setTextDir(android.text.TextDirectionHeuristic);
+ method public android.text.StaticLayout.Builder setTextDirection(android.text.TextDirectionHeuristic);
}
public abstract interface TextDirectionHeuristic {
diff --git a/cmds/telecom/src/com/android/commands/telecom/Telecom.java b/cmds/telecom/src/com/android/commands/telecom/Telecom.java
index 250b002..cdf7429 100644
--- a/cmds/telecom/src/com/android/commands/telecom/Telecom.java
+++ b/cmds/telecom/src/com/android/commands/telecom/Telecom.java
@@ -20,6 +20,7 @@
import android.content.Context;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.telecom.PhoneAccount;
import android.telecom.PhoneAccountHandle;
import android.text.TextUtils;
@@ -39,8 +40,10 @@
(new Telecom()).run(args);
}
- private static final String COMMAND_SET_PHONE_ACOUNT_ENABLED = "set-phone-account-enabled";
- private static final String COMMAND_SET_PHONE_ACOUNT_DISABLED = "set-phone-account-disabled";
+ private static final String COMMAND_SET_PHONE_ACCOUNT_ENABLED = "set-phone-account-enabled";
+ private static final String COMMAND_SET_PHONE_ACCOUNT_DISABLED = "set-phone-account-disabled";
+ private static final String COMMAND_REGISTER_PHONE_ACCOUNT = "register-phone-account";
+ private static final String COMMAND_UNREGISTER_PHONE_ACCOUNT = "unregister-phone-account";
private static final String COMMAND_SET_DEFAULT_DIALER = "set-default-dialer";
private static final String COMMAND_GET_DEFAULT_DIALER = "get-default-dialer";
@@ -54,6 +57,8 @@
"usage: telecom [subcommand] [options]\n" +
"usage: telecom set-phone-account-enabled <COMPONENT> <ID>\n" +
"usage: telecom set-phone-account-disabled <COMPONENT> <ID>\n" +
+ "usage: telecom register-phone-account <COMPONENT> <ID> <LABEL>\n" +
+ "usage: telecom unregister-phone-account <COMPONENT> <ID>\n" +
"usage: telecom set-default-dialer <PACKAGE>\n" +
"usage: telecom get-default-dialer <PACKAGE>\n" +
"\n" +
@@ -63,9 +68,9 @@
"telecom set-phone-account-disabled: Disables the given phone account, if it \n" +
" has already been registered with telecom.\n" +
"\n" +
- "telecom set-default_dialer: Sets the default dialer to the given component. \n" +
+ "telecom set-default-dialer: Sets the default dialer to the given component. \n" +
"\n" +
- "telecom get-default_dialer: Displays the current default dialer. \n"
+ "telecom get-default-dialer: Displays the current default dialer. \n"
);
}
@@ -80,12 +85,18 @@
String command = nextArgRequired();
switch (command) {
- case COMMAND_SET_PHONE_ACOUNT_ENABLED:
+ case COMMAND_SET_PHONE_ACCOUNT_ENABLED:
runSetPhoneAccountEnabled(true);
break;
- case COMMAND_SET_PHONE_ACOUNT_DISABLED:
+ case COMMAND_SET_PHONE_ACCOUNT_DISABLED:
runSetPhoneAccountEnabled(false);
break;
+ case COMMAND_REGISTER_PHONE_ACCOUNT:
+ runRegisterPhoneAccount();
+ break;
+ case COMMAND_UNREGISTER_PHONE_ACCOUNT:
+ runUnregisterPhoneAccount();
+ break;
case COMMAND_SET_DEFAULT_DIALER:
runSetDefaultDialer();
break;
@@ -98,9 +109,7 @@
}
private void runSetPhoneAccountEnabled(boolean enabled) throws RemoteException {
- final ComponentName component = parseComponentName(nextArgRequired());
- final String accountId = nextArgRequired();
- final PhoneAccountHandle handle = new PhoneAccountHandle(component, accountId);
+ final PhoneAccountHandle handle = getPhoneAccountHandleFromArgs();
final boolean success = mTelecomService.enablePhoneAccount(handle, enabled);
if (success) {
System.out.println("Success - " + handle + (enabled ? " enabled." : " disabled."));
@@ -109,6 +118,21 @@
}
}
+ private void runRegisterPhoneAccount() throws RemoteException {
+ final PhoneAccountHandle handle = getPhoneAccountHandleFromArgs();
+ final String label = nextArgRequired();
+ PhoneAccount account = PhoneAccount.builder(handle, label)
+ .setCapabilities(PhoneAccount.CAPABILITY_CALL_PROVIDER).build();
+ mTelecomService.registerPhoneAccount(account);
+ System.out.println("Success - " + handle + " registered.");
+ }
+
+ private void runUnregisterPhoneAccount() throws RemoteException {
+ final PhoneAccountHandle handle = getPhoneAccountHandleFromArgs();
+ mTelecomService.unregisterPhoneAccount(handle);
+ System.out.println("Success - " + handle + " unregistered.");
+ }
+
private void runSetDefaultDialer() throws RemoteException {
final String packageName = nextArgRequired();
final boolean success = mTelecomService.setDefaultDialer(packageName);
@@ -124,6 +148,12 @@
System.out.println(mTelecomService.getDefaultDialerPackage());
}
+ private PhoneAccountHandle getPhoneAccountHandleFromArgs() {
+ final ComponentName component = parseComponentName(nextArgRequired());
+ final String accountId = nextArgRequired();
+ return new PhoneAccountHandle(component, accountId);
+ }
+
private ComponentName parseComponentName(String component) {
ComponentName cn = ComponentName.unflattenFromString(component);
if (cn == null) {
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 073fefc..90567c7 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -38,6 +38,7 @@
import android.annotation.SystemApi;
import android.app.admin.DevicePolicyManager;
+import android.app.assist.AssistContent;
import android.content.ComponentCallbacks2;
import android.content.ComponentName;
import android.content.ContentResolver;
diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java
index e4def1e..b6cec60 100644
--- a/core/java/android/app/ActivityManagerNative.java
+++ b/core/java/android/app/ActivityManagerNative.java
@@ -17,6 +17,8 @@
package android.app;
import android.app.ActivityManager.StackInfo;
+import android.app.assist.AssistContent;
+import android.app.assist.AssistStructure;
import android.content.ComponentName;
import android.content.IIntentReceiver;
import android.content.IIntentSender;
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 2a98b6c..3224d41 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -16,6 +16,8 @@
package android.app;
+import android.app.assist.AssistContent;
+import android.app.assist.AssistStructure;
import android.app.backup.BackupAgent;
import android.content.BroadcastReceiver;
import android.content.ComponentCallbacks2;
diff --git a/core/java/android/app/AssistContent.java b/core/java/android/app/AssistContent.java
index f271af1..4cb89a8 100644
--- a/core/java/android/app/AssistContent.java
+++ b/core/java/android/app/AssistContent.java
@@ -26,9 +26,11 @@
/**
* Holds information about the content an application is viewing, to hand to an
* assistant at the user's request. This is filled in by
- * {@link Activity#onProvideAssistContent Activity.onProvideAssistContent}.
+ * {@link android.app.Activity#onProvideAssistContent Activity.onProvideAssistContent}.
+ * @deprecated use {@link android.app.assist.AssistContent}.
*/
-public class AssistContent implements Parcelable {
+@Deprecated
+public class AssistContent {
private Intent mIntent;
private ClipData mClipData;
private Uri mUri;
@@ -36,16 +38,16 @@
/**
* @hide
* Key name this data structure is stored in the Bundle generated by
- * {@link Activity#onProvideAssistData}.
+ * {@link android.app.Activity#onProvideAssistData}.
*/
public static final String ASSIST_KEY = "android:assist_content";
/**
* @hide
* Retrieve the framework-generated AssistContent that is stored within
- * the Bundle filled in by {@link Activity#onProvideAssistContent}.
+ * the Bundle filled in by {@link android.app.Activity#onProvideAssistContent}.
*/
- public static AssistContent getAssistContent(Bundle assistBundle) {
+ public static android.app.assist.AssistContent getAssistContent(Bundle assistBundle) {
return assistBundle.getParcelable(ASSIST_KEY);
}
@@ -71,6 +73,7 @@
/**
* Return the current {@link #setIntent}, which you can modify in-place.
+ * @hide
*/
public Intent getIntent() {
return mIntent;
@@ -116,7 +119,8 @@
return mUri;
}
- AssistContent(Parcel in) {
+ /** @hide */
+ public AssistContent(Parcel in) {
if (in.readInt() != 0) {
mIntent = Intent.CREATOR.createFromParcel(in);
}
@@ -128,13 +132,8 @@
}
}
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
+ /** @hide */
+ public void writeToParcelInternal(Parcel dest, int flags) {
if (mIntent != null) {
dest.writeInt(1);
mIntent.writeToParcel(dest, flags);
@@ -154,15 +153,4 @@
dest.writeInt(0);
}
}
-
- public static final Parcelable.Creator<AssistContent> CREATOR
- = new Parcelable.Creator<AssistContent>() {
- public AssistContent createFromParcel(Parcel in) {
- return new AssistContent(in);
- }
-
- public AssistContent[] newArray(int size) {
- return new AssistContent[size];
- }
- };
}
diff --git a/core/java/android/app/AssistStructure.java b/core/java/android/app/AssistStructure.java
index ca47a5e..ef7fde4 100644
--- a/core/java/android/app/AssistStructure.java
+++ b/core/java/android/app/AssistStructure.java
@@ -17,9 +17,7 @@
package android.app;
import android.content.ComponentName;
-import android.graphics.Paint;
import android.graphics.Rect;
-import android.graphics.Typeface;
import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
@@ -29,7 +27,6 @@
import android.os.PooledStringWriter;
import android.os.RemoteException;
import android.os.SystemClock;
-import android.text.TextPaint;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
@@ -42,19 +39,22 @@
/**
* Assist data automatically created by the platform's implementation
- * of {@link Activity#onProvideAssistData}.
+ * of {@link android.app.Activity#onProvideAssistData}.
+ * @deprecated use {@link android.app.assist.AssistStructure}.
*/
-final public class AssistStructure implements Parcelable {
+@Deprecated
+public class AssistStructure {
static final String TAG = "AssistStructure";
/**
* @hide
* Key name this data structure is stored in the Bundle generated by
- * {@link Activity#onProvideAssistData}.
+ * {@link android.app.Activity#onProvideAssistData}.
*/
public static final String ASSIST_KEY = "android:assist_structure";
- boolean mHaveData;
+ /** @hide */
+ public boolean mHaveData;
ComponentName mActivityComponent;
@@ -62,15 +62,18 @@
final ArrayList<ViewNodeBuilder> mPendingAsyncChildren = new ArrayList<>();
- SendChannel mSendChannel;
- IBinder mReceiveChannel;
+ /** @hide */
+ public SendChannel mSendChannel;
+ /** @hide */
+ public IBinder mReceiveChannel;
Rect mTmpRect = new Rect();
static final int TRANSACTION_XFER = Binder.FIRST_CALL_TRANSACTION+1;
static final String DESCRIPTOR = "android.app.AssistStructure";
- final class SendChannel extends Binder {
+ /** @hide */
+ public final class SendChannel extends Binder {
@Override protected boolean onTransact(int code, Parcel data, Parcel reply, int flags)
throws RemoteException {
if (code == TRANSACTION_XFER) {
@@ -702,7 +705,8 @@
}
}
- AssistStructure(Activity activity) {
+ /** @hide */
+ public AssistStructure(Activity activity) {
mHaveData = true;
mActivityComponent = activity.getComponentName();
ArrayList<ViewRootImpl> views = WindowManagerGlobal.getInstance().getRootViews(
@@ -713,12 +717,13 @@
}
}
- AssistStructure() {
+ public AssistStructure() {
mHaveData = true;
mActivityComponent = null;
}
- AssistStructure(Parcel in) {
+ /** @hide */
+ public AssistStructure(Parcel in) {
mReceiveChannel = in.readStrongBinder();
}
@@ -792,7 +797,7 @@
* Retrieve the framework-generated AssistStructure that is stored within
* the Bundle filled in by {@link Activity#onProvideAssistData}.
*/
- public static AssistStructure getAssistStructure(Bundle assistBundle) {
+ public static android.app.assist.AssistStructure getAssistStructure(Bundle assistBundle) {
return assistBundle.getParcelable(ASSIST_KEY);
}
@@ -812,16 +817,13 @@
/**
* Return one of the windows in the assist data.
* @param index Which window to retrieve, may be 0 to {@link #getWindowNodeCount()}-1.
+ * @hide
*/
public WindowNode getWindowNodeAt(int index) {
ensureData();
return mWindowNodes.get(index);
}
- public int describeContents() {
- return 0;
- }
-
/** @hide */
public void ensureData() {
if (mHaveData) {
@@ -880,29 +882,4 @@
}
//dump();
}
-
- public void writeToParcel(Parcel out, int flags) {
- if (mHaveData) {
- // This object holds its data. We want to write a send channel that the
- // other side can use to retrieve that data.
- if (mSendChannel == null) {
- mSendChannel = new SendChannel();
- }
- out.writeStrongBinder(mSendChannel);
- } else {
- // This object doesn't hold its data, so just propagate along its receive channel.
- out.writeStrongBinder(mReceiveChannel);
- }
- }
-
- public static final Parcelable.Creator<AssistStructure> CREATOR
- = new Parcelable.Creator<AssistStructure>() {
- public AssistStructure createFromParcel(Parcel in) {
- return new AssistStructure(in);
- }
-
- public AssistStructure[] newArray(int size) {
- return new AssistStructure[size];
- }
- };
}
diff --git a/core/java/android/app/BackStackRecord.java b/core/java/android/app/BackStackRecord.java
index 1fb88a9..02e26a5 100644
--- a/core/java/android/app/BackStackRecord.java
+++ b/core/java/android/app/BackStackRecord.java
@@ -1107,6 +1107,7 @@
}
if (enterTransition != null) {
+ enterTransition.removeTarget(state.nonExistentView);
View view = inFragment.getView();
if (view != null) {
view.captureTransitioningViews(enteringViews);
@@ -1115,7 +1116,6 @@
}
enteringViews.add(state.nonExistentView);
// We added this earlier to prevent any views being targeted.
- enterTransition.removeTarget(state.nonExistentView);
addTargets(enterTransition, enteringViews);
}
setSharedElementEpicenter(enterTransition, state);
@@ -1170,7 +1170,7 @@
Transition exitTransition, Transition sharedElementTransition, Fragment inFragment,
boolean isBack) {
boolean overlap = true;
- if (enterTransition != null && exitTransition != null) {
+ if (enterTransition != null && exitTransition != null && inFragment != null) {
overlap = isBack ? inFragment.getAllowReturnTransitionOverlap() :
inFragment.getAllowEnterTransitionOverlap();
}
@@ -1638,7 +1638,7 @@
private void setBackNameOverrides(TransitionState state, ArrayMap<String, View> namedViews,
boolean isEnd) {
- int count = mSharedElementTargetNames.size();
+ int count = mSharedElementTargetNames == null ? 0 : mSharedElementTargetNames.size();
for (int i = 0; i < count; i++) {
String source = mSharedElementSourceNames.get(i);
String originalTarget = mSharedElementTargetNames.get(i);
@@ -1656,7 +1656,7 @@
private void setNameOverrides(TransitionState state, ArrayMap<String, View> namedViews,
boolean isEnd) {
- int count = namedViews.size();
+ int count = namedViews == null ? 0 : namedViews.size();
for (int i = 0; i < count; i++) {
String source = namedViews.keyAt(i);
String target = namedViews.valueAt(i).getTransitionName();
diff --git a/core/java/android/app/Fragment.java b/core/java/android/app/Fragment.java
index 40c5c64..26d4fd4 100644
--- a/core/java/android/app/Fragment.java
+++ b/core/java/android/app/Fragment.java
@@ -1223,6 +1223,33 @@
}
/**
+ * Gets whether you should show UI with rationale for requesting a permission.
+ * You should do this only if you do not have the permission and the context in
+ * which the permission is requested does not clearly communicate to the user
+ * what would be the benefit from granting this permission.
+ * <p>
+ * For example, if you write a camera app, requesting the camera permission
+ * would be expected by the user and no rationale for why it is requested is
+ * needed. If however, the app needs location for tagging photos then a non-tech
+ * savvy user may wonder how location is related to taking photos. In this case
+ * you may choose to show UI with rationale of requesting this permission.
+ * </p>
+ *
+ * @param permission A permission your app wants to request.
+ * @return Whether you can show permission rationale UI.
+ *
+ * @see Context#checkSelfPermission(String)
+ * @see #requestPermissions(String[], int)
+ * @see #onRequestPermissionsResult(int, String[], int[])
+ */
+ public boolean shouldShowRequestPermissionRationale(@NonNull String permission) {
+ if (mHost != null) {
+ mHost.getContext().getPackageManager().shouldShowRequestPermissionRationale(permission);
+ }
+ return false;
+ }
+
+ /**
* @hide Hack so that DialogFragment can make its Dialog before creating
* its views, and the view construction can use the dialog's context for
* inflation. Maybe this should become a public API. Note sure.
diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java
index 0a425ae..249cdb2 100644
--- a/core/java/android/app/IActivityManager.java
+++ b/core/java/android/app/IActivityManager.java
@@ -19,6 +19,8 @@
import android.app.ActivityManager.RunningTaskInfo;
import android.app.ActivityManager.RunningServiceInfo;
import android.app.ActivityManager.StackInfo;
+import android.app.assist.AssistContent;
+import android.app.assist.AssistStructure;
import android.content.ComponentName;
import android.content.ContentProviderNative;
import android.content.IContentProvider;
diff --git a/core/java/android/app/AssistContent.aidl b/core/java/android/app/assist/AssistContent.aidl
similarity index 95%
rename from core/java/android/app/AssistContent.aidl
rename to core/java/android/app/assist/AssistContent.aidl
index a6321bf..24379bb 100644
--- a/core/java/android/app/AssistContent.aidl
+++ b/core/java/android/app/assist/AssistContent.aidl
@@ -14,6 +14,6 @@
* limitations under the License.
*/
-package android.app;
+package android.app.assist;
parcelable AssistContent;
diff --git a/core/java/android/app/assist/AssistContent.java b/core/java/android/app/assist/AssistContent.java
new file mode 100644
index 0000000..c7e7330
--- /dev/null
+++ b/core/java/android/app/assist/AssistContent.java
@@ -0,0 +1,44 @@
+package android.app.assist;
+
+import android.content.Intent;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * New home for AssistContent.
+ */
+public final class AssistContent extends android.app.AssistContent implements Parcelable {
+
+ /** @hide */
+ public AssistContent() {
+ }
+
+ public AssistContent(Parcel in) {
+ super(in);
+ }
+
+ public Intent getIntent() {
+ return super.getIntent();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ writeToParcelInternal(dest, flags);
+ }
+
+ public static final Parcelable.Creator<AssistContent> CREATOR
+ = new Parcelable.Creator<AssistContent>() {
+ public AssistContent createFromParcel(Parcel in) {
+ return new AssistContent(in);
+ }
+
+ public AssistContent[] newArray(int size) {
+ return new AssistContent[size];
+ }
+ };
+}
diff --git a/core/java/android/app/AssistStructure.aidl b/core/java/android/app/assist/AssistStructure.aidl
similarity index 95%
rename from core/java/android/app/AssistStructure.aidl
rename to core/java/android/app/assist/AssistStructure.aidl
index 07fb2453..ae0a34c 100644
--- a/core/java/android/app/AssistStructure.aidl
+++ b/core/java/android/app/assist/AssistStructure.aidl
@@ -14,6 +14,6 @@
* limitations under the License.
*/
-package android.app;
+package android.app.assist;
parcelable AssistStructure;
diff --git a/core/java/android/app/assist/AssistStructure.java b/core/java/android/app/assist/AssistStructure.java
new file mode 100644
index 0000000..1677e95
--- /dev/null
+++ b/core/java/android/app/assist/AssistStructure.java
@@ -0,0 +1,56 @@
+package android.app.assist;
+
+import android.app.Activity;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * New home for AssistStructure.
+ */
+public final class AssistStructure extends android.app.AssistStructure implements Parcelable {
+
+ public AssistStructure() {
+ }
+
+ /** @hide */
+ public AssistStructure(Activity activity) {
+ super(activity);
+ }
+
+ AssistStructure(Parcel in) {
+ super(in);
+ }
+
+ public WindowNode getWindowNodeAt(int index) {
+ return super.getWindowNodeAt(index);
+ }
+
+ public int describeContents() {
+ return 0;
+ }
+
+ public void writeToParcel(Parcel out, int flags) {
+ if (mHaveData) {
+ // This object holds its data. We want to write a send channel that the
+ // other side can use to retrieve that data.
+ if (mSendChannel == null) {
+ mSendChannel = new SendChannel();
+ }
+ out.writeStrongBinder(mSendChannel);
+ } else {
+ // This object doesn't hold its data, so just propagate along its receive channel.
+ out.writeStrongBinder(mReceiveChannel);
+ }
+ }
+
+ public static final Parcelable.Creator<AssistStructure> CREATOR
+ = new Parcelable.Creator<AssistStructure>() {
+ public AssistStructure createFromParcel(Parcel in) {
+ return new AssistStructure(in);
+ }
+
+ public AssistStructure[] newArray(int size) {
+ return new AssistStructure[size];
+ }
+ };
+}
diff --git a/core/java/android/hardware/camera2/CameraCaptureSession.java b/core/java/android/hardware/camera2/CameraCaptureSession.java
index c22ee5f..82d40d3 100644
--- a/core/java/android/hardware/camera2/CameraCaptureSession.java
+++ b/core/java/android/hardware/camera2/CameraCaptureSession.java
@@ -471,6 +471,17 @@
public abstract boolean isReprocessable();
/**
+ * Return if this capture session is constrained high speed session that is created by
+ * {@link CameraDevice#createConstrainedHighSpeedCaptureSession}.
+ *
+ * @return {@code true} if this session is constrained high speed capture session,
+ * {@code false} otherwise.
+ *
+ * @see CameraDevice#createConstrainedHighSpeedCaptureSession
+ */
+ public abstract boolean isConstrainedHighSpeed();
+
+ /**
* Get the input Surface associated with a reprocessable capture session.
*
* <p>Each reprocessable capture session has an input {@link Surface} where the reprocess
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index d5867a9..85e8827 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -626,35 +626,54 @@
new Key<Integer>("android.control.maxRegionsAf", int.class);
/**
- * <p>List of available high speed video size and fps range configurations
- * supported by the camera device, in the format of (width, height, fps_min, fps_max).</p>
- * <p>When HIGH_SPEED_VIDEO is supported in {@link CameraCharacteristics#CONTROL_AVAILABLE_SCENE_MODES android.control.availableSceneModes}, this metadata
- * will list the supported high speed video size and fps range configurations. All the sizes
- * listed in this configuration will be a subset of the sizes reported by {@link android.hardware.camera2.params.StreamConfigurationMap#getOutputSizes } for processed
- * non-stalling formats.</p>
- * <p>For the high speed video use case, where the application will set
- * {@link CaptureRequest#CONTROL_SCENE_MODE android.control.sceneMode} to HIGH_SPEED_VIDEO in capture requests, the application must
+ * <p>List of available high speed video size, fps range and max batch size configurations
+ * supported by the camera device, in the format of (width, height, fps_min, fps_max, batch_size_max).</p>
+ * <p>When CONSTRAINED_HIGH_SPEED_VIDEO is supported in android.control.availableCapabilities,
+ * this metadata will list the supported high speed video size, fps range and max batch size
+ * configurations. All the sizes listed in this configuration will be a subset of the sizes
+ * reported by {@link android.hardware.camera2.params.StreamConfigurationMap#getOutputSizes }
+ * for processed non-stalling formats.</p>
+ * <p>For the high speed video use case, the application must
* select the video size and fps range from this metadata to configure the recording and
* preview streams and setup the recording requests. For example, if the application intends
* to do high speed recording, it can select the maximum size reported by this metadata to
* configure output streams. Once the size is selected, application can filter this metadata
* by selected size and get the supported fps ranges, and use these fps ranges to setup the
* recording requests. Note that for the use case of multiple output streams, application
- * must select one unique size from this metadata to use. Otherwise a request error might
- * occur.</p>
- * <p>For normal video recording use case, where some application will NOT set
- * {@link CaptureRequest#CONTROL_SCENE_MODE android.control.sceneMode} to HIGH_SPEED_VIDEO in capture requests, the fps ranges
- * reported in this metadata must not be used to setup capture requests, or it will cause
- * request error.</p>
+ * must select one unique size from this metadata to use (e.g., preview and recording streams
+ * must have the same size). Otherwise, the high speed capture session creation will fail.</p>
+ * <p>The min and max fps will be multiple times of 30fps.</p>
+ * <p>High speed video streaming extends significant performance pressue to camera hardware,
+ * to achieve efficient high speed streaming, the camera device may have to aggregate
+ * multiple frames together and send to camera device for processing where the request
+ * controls are same for all the frames in this batch. Max batch size indicates
+ * the max possible number of frames the camera device will group together for this high
+ * speed stream configuration. This max batch size will be used to generate a high speed
+ * recording request list by
+ * {@link android.hardware.camera2.CameraDevice#createConstrainedHighSpeedRequestList }.
+ * The max batch size for each configuration will satisfy below conditions:</p>
+ * <ul>
+ * <li>Each max batch size will be a divisor of its corresponding fps_max / 30. For example,
+ * if max_fps is 300, max batch size will only be 1, 2, 5, or 10.</li>
+ * <li>The camera device may choose smaller internal batch size for each configuration, but
+ * the actual batch size will be a divisor of max batch size. For example, if the max batch
+ * size is 8, the actual batch size used by camera device will only be 1, 2, 4, or 8.</li>
+ * <li>The max batch size in each configuration entry must be no larger than 32.</li>
+ * </ul>
+ * <p>The camera device doesn't have to support batch mode to achieve high speed video recording,
+ * in such case, batch_size_max will be reported as 1 in each configuration entry.</p>
+ * <p>This fps ranges in this configuration list can only be used to create requests
+ * that are submitted to a high speed camera capture session created by
+ * {@link android.hardware.camera2.CameraDevice#createConstrainedHighSpeedCaptureSession }.
+ * The fps ranges reported in this metadata must not be used to setup capture requests for
+ * normal capture session, or it will cause request error.</p>
* <p><b>Range of valid values:</b><br></p>
- * <p>For each configuration, the fps_max >= 60fps.</p>
+ * <p>For each configuration, the fps_max >= 120fps.</p>
* <p><b>Optional</b> - This value may be {@code null} on some devices.</p>
* <p><b>Limited capability</b> -
* Present on all camera devices that report being at least {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL_LIMITED HARDWARE_LEVEL_LIMITED} devices in the
* {@link CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL android.info.supportedHardwareLevel} key</p>
*
- * @see CameraCharacteristics#CONTROL_AVAILABLE_SCENE_MODES
- * @see CaptureRequest#CONTROL_SCENE_MODE
* @see CameraCharacteristics#INFO_SUPPORTED_HARDWARE_LEVEL
* @hide
*/
@@ -1308,7 +1327,8 @@
* the max pipeline depth.</p>
* <p>A pipeline depth of X stages is equivalent to a pipeline latency of
* X frame intervals.</p>
- * <p>This value will be 8 or less.</p>
+ * <p>This value will normally be 8 or less, however, for high speed capture session,
+ * the max pipeline depth will be up to 8 x size of high speed capture request list.</p>
* <p>This key is available on all devices.</p>
*
* @see CaptureResult#REQUEST_PIPELINE_DEPTH
@@ -1371,6 +1391,7 @@
* <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE BURST_CAPTURE}</li>
* <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING YUV_REPROCESSING}</li>
* <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_DEPTH_OUTPUT DEPTH_OUTPUT}</li>
+ * <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_CONSTRAINED_HIGH_SPEED_VIDEO CONSTRAINED_HIGH_SPEED_VIDEO}</li>
* </ul></p>
* <p>This key is available on all devices.</p>
*
@@ -1384,6 +1405,7 @@
* @see #REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE
* @see #REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING
* @see #REQUEST_AVAILABLE_CAPABILITIES_DEPTH_OUTPUT
+ * @see #REQUEST_AVAILABLE_CAPABILITIES_CONSTRAINED_HIGH_SPEED_VIDEO
*/
@PublicKey
public static final Key<int[]> REQUEST_AVAILABLE_CAPABILITIES =
diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java
index d02f349..006030c 100644
--- a/core/java/android/hardware/camera2/CameraDevice.java
+++ b/core/java/android/hardware/camera2/CameraDevice.java
@@ -583,6 +583,147 @@
throws CameraAccessException;
/**
+ * <p>Create a new constrained high speed capture session.</p>
+ *
+ * <p>The application can use normal capture session (created via {@link #createCaptureSession})
+ * for high speed capture if the desired high speed FPS ranges are advertised by
+ * {@link CameraCharacteristics#CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES}, in which case all API
+ * semantics associated with normal capture sessions applies.</p>
+ *
+ * <p>The method creates a specialized capture session that is only targeted at high speed
+ * video recording (>=120fps) use case if the camera device supports high speed video
+ * capability (i.e., {@link CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES} contains
+ * {@link CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_CONSTRAINED_HIGH_SPEED_VIDEO}).
+ * Therefore, it has special characteristics compared with a normal capture session:</p>
+ *
+ * <ul>
+ *
+ * <li>In addition to the output target Surface requirements specified by the
+ * {@link #createCaptureSession} method, an active high speed capture session will support up
+ * to 2 output Surfaces, though the application might choose to configure just one Surface
+ * (e.g., preview only). All Surfaces must be either video encoder surfaces (acquired by
+ * {@link android.media.MediaRecorder#getSurface} or
+ * {@link android.media.MediaCodec#createInputSurface}) or preview surfaces (obtained from
+ * {@link android.view.SurfaceView}, {@link android.graphics.SurfaceTexture} via
+ * {@link android.view.Surface#Surface(android.graphics.SurfaceTexture)}). The Surface sizes
+ * must be one of the sizes reported by {@link StreamConfigurationMap#getHighSpeedVideoSizes}.
+ * When multiple Surfaces are configured, their size must be same.</li>
+ *
+ * <li>An active high speed capture session only accepts request lists created via
+ * {@link #createConstrainedHighSpeedRequestList}, and the request list can only be submitted
+ * to this session via {@link CameraCaptureSession#captureBurst captureBurst}, or
+ * {@link CameraCaptureSession#setRepeatingBurst setRepeatingBurst}.</li>
+ *
+ * <li>The FPS ranges being requested to this session must be selected from
+ * {@link StreamConfigurationMap#getHighSpeedVideoFpsRangesFor}. The application can still use
+ * {@link CaptureRequest#CONTROL_AE_TARGET_FPS_RANGE} to control the desired FPS range.
+ * Switching to an FPS range that has different
+ * {@link android.util.Range#getUpper() maximum FPS} may trigger some camera device
+ * reconfigurations, which may introduce extra latency. It is recommended that the
+ * application avoids unnecessary maximum target FPS changes as much as possible during high
+ * speed streaming.</li>
+ *
+ * <li>For the request lists submitted to this session, the camera device will override the
+ * {@link CaptureRequest#CONTROL_MODE control mode}, auto-exposure (AE), auto-white balance
+ * (AWB) and auto-focus (AF) to {@link CameraMetadata#CONTROL_MODE_AUTO},
+ * {@link CameraMetadata#CONTROL_AE_MODE_ON}, {@link CameraMetadata#CONTROL_AWB_MODE_AUTO}
+ * and {@link CameraMetadata#CONTROL_AF_MODE_CONTINUOUS_VIDEO}, respectively. All
+ * post-processing block mode controls will be overridden to be FAST. Therefore, no manual
+ * control of capture and post-processing parameters is possible. Beside these, only a subset
+ * of controls will work, see
+ * {@link CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_CONSTRAINED_HIGH_SPEED_VIDEO} for
+ * more details.</li>
+ *
+ * </ul>
+ *
+ * @param outputs The new set of Surfaces that should be made available as
+ * targets for captured high speed 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 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, or the camera device doesn't support
+ * high speed video capability.
+ * @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 #createCaptureSession
+ * @see CaptureRequest#CONTROL_AE_TARGET_FPS_RANGE
+ * @see StreamConfigurationMap#getHighSpeedVideoSizes
+ * @see StreamConfigurationMap#getHighSpeedVideoFpsRangesFor
+ * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
+ * @see CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_CONSTRAINED_HIGH_SPEED_VIDEO
+ * @see CameraCaptureSession#captureBurst
+ * @see CameraCaptureSession#setRepeatingBurst
+ * @see #createConstrainedHighSpeedRequestList
+ */
+ public abstract void createConstrainedHighSpeedCaptureSession(@NonNull List<Surface> outputs,
+ @NonNull CameraCaptureSession.StateCallback callback,
+ @Nullable Handler handler)
+ throws CameraAccessException;
+
+
+ /**
+ * <p>Create a unmodifiable list of requests that is suitable for constrained high speed capture
+ * session streaming.</p>
+ *
+ * <p>High speed video streaming creates significant performance pressue on the camera device,
+ * so to achieve efficient high speed streaming, the camera device may have to aggregate
+ * multiple frames together. This means requests must be sent in batched groups, with all
+ * requests sharing the same settings. This method takes the list of output target
+ * Surfaces (subject to the output Surface requirements specified by the contrained high speed
+ * session) and a {@link CaptureRequest request}, and generates a request list that has the same
+ * controls for each request. The input {@link CaptureRequest request} must contain the target
+ * output Surfaces and target high speed FPS range that is one of the
+ * {@link StreamConfigurationMap#getHighSpeedVideoFpsRangesFor} for the Surface size.</p>
+ *
+ * <p>If both preview and recording Surfaces are specified in the {@code request}, the
+ * {@link CaptureRequest#CONTROL_AE_TARGET_FPS_RANGE target FPS range} in the input
+ * {@link CaptureRequest request} must be a fixed framerate FPS range, where the
+ * {@link android.util.Range#getLower minimal FPS} ==
+ * {@link android.util.Range#getUpper() maximum FPS}. The created request list will contain
+ * a interleaved request pattern such that the preview output FPS is at least 30fps, the
+ * recording output FPS is {@link android.util.Range#getUpper() maximum FPS} of the requested
+ * FPS range. The application can submit this request list directly to an active high speed
+ * capture session to achieve high speed video recording. When only preview or recording
+ * Surface is specified, this method will return a list of request that have the same controls
+ * and output targets for all requests.</p>
+ *
+ * <p>Submitting a request list created by this method to a normal capture session will result
+ * in an {@link IllegalArgumentException} if the high speed
+ * {@link CaptureRequest#CONTROL_AE_TARGET_FPS_RANGE FPS range} is not supported by
+ * {@link CameraCharacteristics#CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES}.</p>
+ *
+ * @param request The high speed capture request that will be used to generate the high speed
+ * request list.
+ * @return A unmodifiable CaptureRequest list that is suitable for constrained high speed
+ * capture.
+ *
+ * @throws IllegalArgumentException if the set of output Surfaces in the request do not meet the
+ * high speed video capability requirements, or the camera
+ * device doesn't support high speed video capability, or the
+ * request doesn't meet the high speed video capability
+ * requirements, or the request doesn't contain the required
+ * controls for high speed capture.
+ * @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 #createConstrainedHighSpeedCaptureSession
+ * @see CaptureRequest#CONTROL_AE_TARGET_FPS_RANGE
+ * @see StreamConfigurationMap#getHighSpeedVideoSizes
+ * @see StreamConfigurationMap#getHighSpeedVideoFpsRangesFor
+ * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
+ * @see CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_CONSTRAINED_HIGH_SPEED_VIDEO
+ */
+ @NonNull
+ public abstract List<CaptureRequest> createConstrainedHighSpeedRequestList(
+ @NonNull CaptureRequest request)throws CameraAccessException;
+
+ /**
* <p>Create a {@link CaptureRequest.Builder} for new capture requests,
* initialized with template for a target use case. The settings are chosen
* to be the best options for the specific camera device, so it is not
diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index ac29f80..f8db6d9 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -648,6 +648,100 @@
*/
public static final int REQUEST_AVAILABLE_CAPABILITIES_DEPTH_OUTPUT = 8;
+ /**
+ * <p>The device supports constrained high speed video recording (frame rate >=120fps)
+ * use case. The camera device will support high speed capture session created by
+ * {@link android.hardware.camera2.CameraDevice#createConstrainedHighSpeedCaptureSession }, which
+ * only accepts high speed request list created by
+ * {@link android.hardware.camera2.CameraDevice#createConstrainedHighSpeedRequestList }.</p>
+ * <p>A camera device can still support high speed video streaming by advertising the high speed
+ * FPS ranges in {@link CameraCharacteristics#CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES android.control.aeAvailableTargetFpsRanges}. For this case, all normal
+ * capture request per frame control and synchronization requirements will apply to
+ * the high speed fps ranges, the same as all other fps ranges. This capability describes
+ * the capability of a specialized operating mode with many limitations (see below), which
+ * is only targeted at high speed video recording.</p>
+ * <p>The supported high speed video sizes and fps ranges are specified in
+ * {@link android.hardware.camera2.params.StreamConfigurationMap#getHighSpeedVideoFpsRanges }.
+ * To get desired output frame rates, the application is only allowed to select video size
+ * and FPS range combinations provided by
+ * {@link android.hardware.camera2.params.StreamConfigurationMap#getHighSpeedVideoSizes }.
+ * The fps range can be controlled via {@link CaptureRequest#CONTROL_AE_TARGET_FPS_RANGE android.control.aeTargetFpsRange}.</p>
+ * <p>In this capability, the camera device will override aeMode, awbMode, and afMode to
+ * ON, ON, and CONTINUOUS_VIDEO, respectively. All post-processing block mode
+ * controls will be overridden to be FAST. Therefore, no manual control of capture
+ * and post-processing parameters is possible. All other controls operate the
+ * same as when {@link CaptureRequest#CONTROL_MODE android.control.mode} == AUTO. This means that all other
+ * android.control.* fields continue to work, such as</p>
+ * <ul>
+ * <li>{@link CaptureRequest#CONTROL_AE_TARGET_FPS_RANGE android.control.aeTargetFpsRange}</li>
+ * <li>{@link CaptureRequest#CONTROL_AE_EXPOSURE_COMPENSATION android.control.aeExposureCompensation}</li>
+ * <li>{@link CaptureRequest#CONTROL_AE_LOCK android.control.aeLock}</li>
+ * <li>{@link CaptureRequest#CONTROL_AWB_LOCK android.control.awbLock}</li>
+ * <li>{@link CaptureRequest#CONTROL_EFFECT_MODE android.control.effectMode}</li>
+ * <li>{@link CaptureRequest#CONTROL_AE_REGIONS android.control.aeRegions}</li>
+ * <li>{@link CaptureRequest#CONTROL_AF_REGIONS android.control.afRegions}</li>
+ * <li>{@link CaptureRequest#CONTROL_AWB_REGIONS android.control.awbRegions}</li>
+ * <li>{@link CaptureRequest#CONTROL_AF_TRIGGER android.control.afTrigger}</li>
+ * <li>{@link CaptureRequest#CONTROL_AE_PRECAPTURE_TRIGGER android.control.aePrecaptureTrigger}</li>
+ * </ul>
+ * <p>Outside of android.control.*, the following controls will work:</p>
+ * <ul>
+ * <li>{@link CaptureRequest#FLASH_MODE android.flash.mode} (TORCH mode only, automatic flash for still capture will not
+ * work since aeMode is ON)</li>
+ * <li>{@link CaptureRequest#LENS_OPTICAL_STABILIZATION_MODE android.lens.opticalStabilizationMode} (if it is supported)</li>
+ * <li>{@link CaptureRequest#SCALER_CROP_REGION android.scaler.cropRegion}</li>
+ * <li>{@link CaptureRequest#STATISTICS_FACE_DETECT_MODE android.statistics.faceDetectMode} (if it is supported)</li>
+ * </ul>
+ * <p>For high speed recording use case, the actual maximum supported frame rate may
+ * be lower than what camera can output, depending on the destination Surfaces for
+ * the image data. For example, if the destination surface is from video encoder,
+ * the application need check if the video encoder is capable of supporting the
+ * high frame rate for a given video size, or it will end up with lower recording
+ * frame rate. If the destination surface is from preview window, the actual preview frame
+ * rate will be bounded by the screen refresh rate.</p>
+ * <p>The camera device will only support up to 2 high speed simultaneous output surfaces
+ * (preview and recording surfaces)
+ * in this mode. Above controls will be effective only if all of below conditions are true:</p>
+ * <ul>
+ * <li>The application creates a camera capture session with no more than 2 surfaces via
+ * {@link android.hardware.camera2.CameraDevice#createConstrainedHighSpeedCaptureSession }. The
+ * targeted surfaces must be preview surface (either from
+ * {@link android.view.SurfaceView } or {@link android.graphics.SurfaceTexture }) or
+ * recording surface(either from {@link android.media.MediaRecorder#getSurface } or
+ * {@link android.media.MediaCodec#createInputSurface }).</li>
+ * <li>The stream sizes are selected from the sizes reported by
+ * {@link android.hardware.camera2.params.StreamConfigurationMap#getHighSpeedVideoSizes }.</li>
+ * <li>The FPS ranges are selected from
+ * {@link android.hardware.camera2.params.StreamConfigurationMap#getHighSpeedVideoFpsRanges }.</li>
+ * </ul>
+ * <p>When above conditions are NOT satistied, the
+ * {@link android.hardware.camera2.CameraDevice#createConstrainedHighSpeedCaptureSession }
+ * and {@link android.hardware.camera2.CameraDevice#createConstrainedHighSpeedRequestList } will fail.</p>
+ * <p>Switching to a FPS range that has different maximum FPS may trigger some camera device
+ * reconfigurations, which may introduce extra latency. It is recommended that
+ * the application avoids unnecessary maximum target FPS changes as much as possible
+ * during high speed streaming.</p>
+ *
+ * @see CameraCharacteristics#CONTROL_AE_AVAILABLE_TARGET_FPS_RANGES
+ * @see CaptureRequest#CONTROL_AE_EXPOSURE_COMPENSATION
+ * @see CaptureRequest#CONTROL_AE_LOCK
+ * @see CaptureRequest#CONTROL_AE_PRECAPTURE_TRIGGER
+ * @see CaptureRequest#CONTROL_AE_REGIONS
+ * @see CaptureRequest#CONTROL_AE_TARGET_FPS_RANGE
+ * @see CaptureRequest#CONTROL_AF_REGIONS
+ * @see CaptureRequest#CONTROL_AF_TRIGGER
+ * @see CaptureRequest#CONTROL_AWB_LOCK
+ * @see CaptureRequest#CONTROL_AWB_REGIONS
+ * @see CaptureRequest#CONTROL_EFFECT_MODE
+ * @see CaptureRequest#CONTROL_MODE
+ * @see CaptureRequest#FLASH_MODE
+ * @see CaptureRequest#LENS_OPTICAL_STABILIZATION_MODE
+ * @see CaptureRequest#SCALER_CROP_REGION
+ * @see CaptureRequest#STATISTICS_FACE_DETECT_MODE
+ * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
+ */
+ public static final int REQUEST_AVAILABLE_CAPABILITIES_CONSTRAINED_HIGH_SPEED_VIDEO = 9;
+
//
// Enumeration values for CameraCharacteristics#SCALER_CROPPING_TYPE
//
@@ -1725,6 +1819,10 @@
public static final int CONTROL_SCENE_MODE_BARCODE = 16;
/**
+ * <p>This is deprecated, please use
+ * {@link android.hardware.camera2.CameraDevice#createConstrainedHighSpeedCaptureSession }
+ * and {@link android.hardware.camera2.CameraDevice#createConstrainedHighSpeedRequestList }
+ * for high speed video recording.</p>
* <p>Optimized for high speed video recording (frame rate >=60fps) use case.</p>
* <p>The supported high speed video sizes and fps ranges are specified in
* android.control.availableHighSpeedVideoConfigurations. To get desired
@@ -1799,6 +1897,7 @@
* @see CaptureRequest#SCALER_CROP_REGION
* @see CaptureRequest#STATISTICS_FACE_DETECT_MODE
* @see CaptureRequest#CONTROL_SCENE_MODE
+ * @deprecated Please refer to this API documentation to find the alternatives
*/
public static final int CONTROL_SCENE_MODE_HIGH_SPEED_VIDEO = 17;
diff --git a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
index 7a39dd5..ab0f607 100644
--- a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
@@ -721,4 +721,10 @@
}
}
+ @Override
+ public boolean isConstrainedHighSpeed() {
+ // TODO: to be implemented
+ return false;
+ }
+
}
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index e60e266..16701e5 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -1906,4 +1906,18 @@
private CameraCharacteristics getCharacteristics() {
return mCharacteristics;
}
+
+ @Override
+ public void createConstrainedHighSpeedCaptureSession(List<Surface> outputs,
+ android.hardware.camera2.CameraCaptureSession.StateCallback callback, Handler handler)
+ throws CameraAccessException {
+ // TODO: to be implemented
+ throw new UnsupportedOperationException("To be implemented!!!!");
+ }
+
+ @Override
+ public List<CaptureRequest> createConstrainedHighSpeedRequestList(CaptureRequest request)
+ throws CameraAccessException {
+ throw new UnsupportedOperationException("To be implemented!!!!");
+ }
}
diff --git a/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableHighSpeedVideoConfiguration.java b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableHighSpeedVideoConfiguration.java
index c03144b..2449abe 100644
--- a/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableHighSpeedVideoConfiguration.java
+++ b/core/java/android/hardware/camera2/marshal/impl/MarshalQueryableHighSpeedVideoConfiguration.java
@@ -33,7 +33,7 @@
*/
public class MarshalQueryableHighSpeedVideoConfiguration
implements MarshalQueryable<HighSpeedVideoConfiguration> {
- private static final int SIZE = SIZEOF_INT32 * 4;
+ private static final int SIZE = SIZEOF_INT32 * 5;
private class MarshalerHighSpeedVideoConfiguration
extends Marshaler<HighSpeedVideoConfiguration> {
@@ -49,6 +49,7 @@
buffer.putInt(value.getHeight());
buffer.putInt(value.getFpsMin());
buffer.putInt(value.getFpsMax());
+ buffer.putInt(value.getBatchSizeMax());
}
@Override
@@ -57,8 +58,9 @@
int height = buffer.getInt();
int fpsMin = buffer.getInt();
int fpsMax = buffer.getInt();
+ int batchSizeMax = buffer.getInt();
- return new HighSpeedVideoConfiguration(width, height, fpsMin, fpsMax);
+ return new HighSpeedVideoConfiguration(width, height, fpsMin, fpsMax, batchSizeMax);
}
@Override
diff --git a/core/java/android/hardware/camera2/params/HighSpeedVideoConfiguration.java b/core/java/android/hardware/camera2/params/HighSpeedVideoConfiguration.java
index 088049f..b469126 100644
--- a/core/java/android/hardware/camera2/params/HighSpeedVideoConfiguration.java
+++ b/core/java/android/hardware/camera2/params/HighSpeedVideoConfiguration.java
@@ -33,6 +33,7 @@
* @hide
*/
public final class HighSpeedVideoConfiguration {
+ static final private int HIGH_SPEED_MAX_MINIMAL_FPS = 120;
/**
* Create a new {@link HighSpeedVideoConfiguration}.
@@ -48,15 +49,18 @@
* @hide
*/
public HighSpeedVideoConfiguration(
- final int width, final int height, final int fpsMin, final int fpsMax) {
- if (fpsMax < 60) {
- throw new IllegalArgumentException("fpsMax must be at least 60");
+ final int width, final int height, final int fpsMin, final int fpsMax,
+ final int batchSizeMax) {
+ if (fpsMax < HIGH_SPEED_MAX_MINIMAL_FPS) {
+ throw new IllegalArgumentException("fpsMax must be at least " +
+ HIGH_SPEED_MAX_MINIMAL_FPS);
}
mFpsMax = fpsMax;
mWidth = checkArgumentPositive(width, "width must be positive");
mHeight = checkArgumentPositive(height, "height must be positive");
mFpsMin = checkArgumentPositive(fpsMin, "fpsMin must be positive");
mSize = new Size(mWidth, mHeight);
+ mBatchSizeMax = checkArgumentPositive(batchSizeMax, "batchSizeMax must be positive");
mFpsRange = new Range<Integer>(mFpsMin, mFpsMax);
}
@@ -106,9 +110,18 @@
}
/**
+ * Convenience method to return the max batch size of this high speed video configuration.
+ *
+ * @return the maximal batch size for this high speed video configuration
+ */
+ public int getBatchSizeMax() {
+ return mBatchSizeMax;
+ }
+
+ /**
* Convenience method to return the FPS range of this high speed video configuration.
*
- * @return a Range with high bound >= 60
+ * @return a Range with high bound >= {@value #HIGH_SPEED_MAX_MINIMAL_FPS}
*/
public Range<Integer> getFpsRange() {
return mFpsRange;
@@ -135,7 +148,8 @@
return mWidth == other.mWidth &&
mHeight == other.mHeight &&
mFpsMin == other.mFpsMin &&
- mFpsMax == other.mFpsMax;
+ mFpsMax == other.mFpsMax &&
+ mBatchSizeMax == other.mBatchSizeMax;
}
return false;
}
@@ -152,6 +166,7 @@
private final int mHeight;
private final int mFpsMin;
private final int mFpsMax;
+ private final int mBatchSizeMax;
private final Size mSize;
private final Range<Integer> mFpsRange;
}
diff --git a/core/java/android/service/voice/IVoiceInteractionSession.aidl b/core/java/android/service/voice/IVoiceInteractionSession.aidl
index 894edac..8fe84e1 100644
--- a/core/java/android/service/voice/IVoiceInteractionSession.aidl
+++ b/core/java/android/service/voice/IVoiceInteractionSession.aidl
@@ -16,8 +16,8 @@
package android.service.voice;
-import android.app.AssistContent;
-import android.app.AssistStructure;
+import android.app.assist.AssistContent;
+import android.app.assist.AssistStructure;
import android.content.Intent;
import android.graphics.Bitmap;
import android.os.Bundle;
diff --git a/core/java/android/service/voice/VoiceInteractionSession.java b/core/java/android/service/voice/VoiceInteractionSession.java
index 48ad5a8..33fef62 100644
--- a/core/java/android/service/voice/VoiceInteractionSession.java
+++ b/core/java/android/service/voice/VoiceInteractionSession.java
@@ -16,11 +16,11 @@
package android.service.voice;
-import android.app.AssistContent;
-import android.app.AssistStructure;
import android.app.Dialog;
import android.app.Instrumentation;
import android.app.VoiceInteractor;
+import android.app.assist.AssistContent;
+import android.app.assist.AssistStructure;
import android.content.ComponentCallbacks2;
import android.content.Context;
import android.content.Intent;
@@ -386,7 +386,7 @@
}
/**
- * ASk the app to cancel this current request.
+ * ASk the app to cancelLocked this current request.
*/
public void cancel() {
try {
@@ -878,14 +878,34 @@
show(null, 0);
}
- public void show(Bundle args, int showFlags) {
+ /**
+ * Show the UI for this session. This asks the system to go through the process of showing
+ * your UI, which will eventually culminate in {@link #onShow}. This is similar to calling
+ * {@link VoiceInteractionService#showSession VoiceInteractionService.showSession}.
+ * @param args Arbitrary arguments that will be propagated {@link #onShow}.
+ * @param flags Indicates additional optional behavior that should be performed. May
+ * be {@link VoiceInteractionSession#SHOW_WITH_ASSIST VoiceInteractionSession.SHOW_WITH_ASSIST}
+ * to request that the system generate and deliver assist data on the current foreground
+ * app as part of showing the session UI.
+ */
+ public void show(Bundle args, int flags) {
+ if (mToken == null) {
+ throw new IllegalStateException("Can't call before onCreate()");
+ }
try {
- mSystemService.showSessionFromSession(mToken, null, 0);
+ mSystemService.showSessionFromSession(mToken, args, flags);
} catch (RemoteException e) {
}
}
+ /**
+ * Hide the session's UI, if currently shown. Call this when you are done with your
+ * user interaction.
+ */
public void hide() {
+ if (mToken == null) {
+ throw new IllegalStateException("Can't call before onCreate()");
+ }
try {
mSystemService.hideSessionFromSession(mToken);
} catch (RemoteException e) {
@@ -964,6 +984,9 @@
* {@link #startVoiceActivity}.</p>
*/
public void setKeepAwake(boolean keepAwake) {
+ if (mToken == null) {
+ throw new IllegalStateException("Can't call before onCreate()");
+ }
try {
mSystemService.setKeepAwake(mToken, keepAwake);
} catch (RemoteException e) {
@@ -985,7 +1008,9 @@
}
/**
- * Finish the session.
+ * Finish the session. This completely destroys the session -- the next time it is shown,
+ * an entirely new one will be created. You do not normally call this function; instead,
+ * use {@link #hide} and allow the system to destroy your session if it needs its RAM.
*/
public void finish() {
if (mToken == null) {
@@ -1114,7 +1139,7 @@
* Called when the user presses the back button while focus is in the session UI. Note
* that this will only happen if the session UI has requested input focus in its window;
* otherwise, the back key will go to whatever window has focus and do whatever behavior
- * it normally has there.
+ * it normally has there. The default implementation simply calls {@link #hide}.
*/
public void onBackPressed() {
hide();
@@ -1123,7 +1148,7 @@
/**
* Sessions automatically watch for requests that all system UI be closed (such as when
* the user presses HOME), which will appear here. The default implementation always
- * calls {@link #finish}.
+ * calls {@link #hide}.
*/
public void onCloseSystemDialogs() {
hide();
@@ -1287,7 +1312,7 @@
}
/**
- * Called when the {@link android.app.VoiceInteractor} has asked to cancel a {@link Request}
+ * Called when the {@link android.app.VoiceInteractor} has asked to cancelLocked a {@link Request}
* that was previously delivered to {@link #onRequestConfirmation},
* {@link #onRequestPickOption}, {@link #onRequestCompleteVoice}, {@link #onRequestAbortVoice},
* or {@link #onRequestCommand}.
diff --git a/core/java/android/text/Annotation.java b/core/java/android/text/Annotation.java
index dbc290b..bb5d3ea 100644
--- a/core/java/android/text/Annotation.java
+++ b/core/java/android/text/Annotation.java
@@ -38,6 +38,11 @@
}
public int getSpanTypeId() {
+ return getSpanTypeIdInternal();
+ }
+
+ /** @hide */
+ public int getSpanTypeIdInternal() {
return TextUtils.ANNOTATION;
}
@@ -46,6 +51,11 @@
}
public void writeToParcel(Parcel dest, int flags) {
+ writeToParcelInternal(dest, flags);
+ }
+
+ /** @hide */
+ public void writeToParcelInternal(Parcel dest, int flags) {
dest.writeString(mKey);
dest.writeString(mValue);
}
diff --git a/core/java/android/text/DynamicLayout.java b/core/java/android/text/DynamicLayout.java
index e99a960..5b5cdd2 100644
--- a/core/java/android/text/DynamicLayout.java
+++ b/core/java/android/text/DynamicLayout.java
@@ -291,7 +291,7 @@
b.setText(text, where, where + after)
.setPaint(getPaint())
.setWidth(getWidth())
- .setTextDir(getTextDirectionHeuristic())
+ .setTextDirection(getTextDirectionHeuristic())
.setLineSpacing(getSpacingAdd(), getSpacingMultiplier())
.setEllipsizedWidth(mEllipsizedWidth)
.setEllipsize(mEllipsizeAt)
diff --git a/core/java/android/text/ParcelableSpan.java b/core/java/android/text/ParcelableSpan.java
index 224511a..d7c1a4b 100644
--- a/core/java/android/text/ParcelableSpan.java
+++ b/core/java/android/text/ParcelableSpan.java
@@ -16,6 +16,7 @@
package android.text;
+import android.os.Parcel;
import android.os.Parcelable;
/**
@@ -27,5 +28,21 @@
/**
* Return a special type identifier for this span class.
*/
- public abstract int getSpanTypeId();
+ int getSpanTypeId();
+
+ /**
+ * Internal implementation of {@link #getSpanTypeId()} that is not meant to
+ * be overridden outside of the framework.
+ *
+ * @hide
+ */
+ int getSpanTypeIdInternal();
+
+ /**
+ * Internal implementation of {@link Parcelable#writeToParcel(Parcel, int)}
+ * that is not meant to be overridden outside of the framework.
+ *
+ * @hide
+ */
+ void writeToParcelInternal(Parcel dest, int flags);
}
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index d6d046b..464710b 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -184,7 +184,7 @@
* @param textDir text direction heuristic for resolving BiDi behavior.
* @return this builder, useful for chaining
*/
- public Builder setTextDir(TextDirectionHeuristic textDir) {
+ public Builder setTextDirection(TextDirectionHeuristic textDir) {
mTextDir = textDir;
return this;
}
@@ -473,7 +473,7 @@
Builder b = Builder.obtain(source, bufstart, bufend, paint, outerwidth)
.setAlignment(align)
- .setTextDir(textDir)
+ .setTextDirection(textDir)
.setLineSpacing(spacingadd, spacingmult)
.setIncludePad(includepad)
.setEllipsizedWidth(ellipsizedWidth)
diff --git a/core/java/android/text/TextUtils.java b/core/java/android/text/TextUtils.java
index 676986d..6c4d8fd 100644
--- a/core/java/android/text/TextUtils.java
+++ b/core/java/android/text/TextUtils.java
@@ -622,8 +622,7 @@
* Flatten a CharSequence and whatever styles can be copied across processes
* into the parcel.
*/
- public static void writeToParcel(CharSequence cs, Parcel p,
- int parcelableFlags) {
+ public static void writeToParcel(CharSequence cs, Parcel p, int parcelableFlags) {
if (cs instanceof Spanned) {
p.writeInt(0);
p.writeString(cs.toString());
@@ -645,15 +644,15 @@
}
if (prop instanceof ParcelableSpan) {
- ParcelableSpan ps = (ParcelableSpan)prop;
- int spanTypeId = ps.getSpanTypeId();
+ final ParcelableSpan ps = (ParcelableSpan) prop;
+ final int spanTypeId = ps.getSpanTypeIdInternal();
if (spanTypeId < FIRST_SPAN || spanTypeId > LAST_SPAN) {
- Log.e(TAG, "external class \"" + ps.getClass().getSimpleName()
+ Log.e(TAG, "External class \"" + ps.getClass().getSimpleName()
+ "\" is attempting to use the frameworks-only ParcelableSpan"
+ " interface");
} else {
p.writeInt(spanTypeId);
- ps.writeToParcel(p, parcelableFlags);
+ ps.writeToParcelInternal(p, parcelableFlags);
writeWhere(p, sp, o);
}
}
diff --git a/core/java/android/text/style/AbsoluteSizeSpan.java b/core/java/android/text/style/AbsoluteSizeSpan.java
index 1214040..908ef55 100644
--- a/core/java/android/text/style/AbsoluteSizeSpan.java
+++ b/core/java/android/text/style/AbsoluteSizeSpan.java
@@ -49,6 +49,11 @@
}
public int getSpanTypeId() {
+ return getSpanTypeIdInternal();
+ }
+
+ /** @hide */
+ public int getSpanTypeIdInternal() {
return TextUtils.ABSOLUTE_SIZE_SPAN;
}
@@ -57,6 +62,11 @@
}
public void writeToParcel(Parcel dest, int flags) {
+ writeToParcelInternal(dest, flags);
+ }
+
+ /** @hide */
+ public void writeToParcelInternal(Parcel dest, int flags) {
dest.writeInt(mSize);
dest.writeInt(mDip ? 1 : 0);
}
diff --git a/core/java/android/text/style/AlignmentSpan.java b/core/java/android/text/style/AlignmentSpan.java
index b8a37da..6158309 100644
--- a/core/java/android/text/style/AlignmentSpan.java
+++ b/core/java/android/text/style/AlignmentSpan.java
@@ -22,10 +22,9 @@
import android.text.TextUtils;
public interface AlignmentSpan extends ParagraphStyle {
- public Layout.Alignment getAlignment();
+ Layout.Alignment getAlignment();
- public static class Standard
- implements AlignmentSpan, ParcelableSpan {
+ class Standard implements AlignmentSpan, ParcelableSpan {
public Standard(Layout.Alignment align) {
mAlignment = align;
}
@@ -35,6 +34,11 @@
}
public int getSpanTypeId() {
+ return getSpanTypeIdInternal();
+ }
+
+ /** @hide */
+ public int getSpanTypeIdInternal() {
return TextUtils.ALIGNMENT_SPAN;
}
@@ -43,6 +47,11 @@
}
public void writeToParcel(Parcel dest, int flags) {
+ writeToParcelInternal(dest, flags);
+ }
+
+ /** @hide */
+ public void writeToParcelInternal(Parcel dest, int flags) {
dest.writeString(mAlignment.name());
}
diff --git a/core/java/android/text/style/BackgroundColorSpan.java b/core/java/android/text/style/BackgroundColorSpan.java
index cda8015..de05f50 100644
--- a/core/java/android/text/style/BackgroundColorSpan.java
+++ b/core/java/android/text/style/BackgroundColorSpan.java
@@ -35,6 +35,11 @@
}
public int getSpanTypeId() {
+ return getSpanTypeIdInternal();
+ }
+
+ /** @hide */
+ public int getSpanTypeIdInternal() {
return TextUtils.BACKGROUND_COLOR_SPAN;
}
@@ -43,6 +48,11 @@
}
public void writeToParcel(Parcel dest, int flags) {
+ writeToParcelInternal(dest, flags);
+ }
+
+ /** @hide */
+ public void writeToParcelInternal(Parcel dest, int flags) {
dest.writeInt(mColor);
}
diff --git a/core/java/android/text/style/BulletSpan.java b/core/java/android/text/style/BulletSpan.java
index 3f86b08..7408415 100644
--- a/core/java/android/text/style/BulletSpan.java
+++ b/core/java/android/text/style/BulletSpan.java
@@ -60,6 +60,11 @@
}
public int getSpanTypeId() {
+ return getSpanTypeIdInternal();
+ }
+
+ /** @hide */
+ public int getSpanTypeIdInternal() {
return TextUtils.BULLET_SPAN;
}
@@ -68,6 +73,11 @@
}
public void writeToParcel(Parcel dest, int flags) {
+ writeToParcelInternal(dest, flags);
+ }
+
+ /** @hide */
+ public void writeToParcelInternal(Parcel dest, int flags) {
dest.writeInt(mGapWidth);
dest.writeInt(mWantColor ? 1 : 0);
dest.writeInt(mColor);
diff --git a/core/java/android/text/style/EasyEditSpan.java b/core/java/android/text/style/EasyEditSpan.java
index 03b4f60..7af1c2c 100644
--- a/core/java/android/text/style/EasyEditSpan.java
+++ b/core/java/android/text/style/EasyEditSpan.java
@@ -91,12 +91,22 @@
@Override
public void writeToParcel(Parcel dest, int flags) {
+ writeToParcelInternal(dest, flags);
+ }
+
+ /** @hide */
+ public void writeToParcelInternal(Parcel dest, int flags) {
dest.writeParcelable(mPendingIntent, 0);
dest.writeByte((byte) (mDeleteEnabled ? 1 : 0));
}
@Override
public int getSpanTypeId() {
+ return getSpanTypeIdInternal();
+ }
+
+ /** @hide */
+ public int getSpanTypeIdInternal() {
return TextUtils.EASY_EDIT_SPAN;
}
diff --git a/core/java/android/text/style/ForegroundColorSpan.java b/core/java/android/text/style/ForegroundColorSpan.java
index f167aab..2bc6d54 100644
--- a/core/java/android/text/style/ForegroundColorSpan.java
+++ b/core/java/android/text/style/ForegroundColorSpan.java
@@ -36,14 +36,24 @@
}
public int getSpanTypeId() {
+ return getSpanTypeIdInternal();
+ }
+
+ /** @hide */
+ public int getSpanTypeIdInternal() {
return TextUtils.FOREGROUND_COLOR_SPAN;
}
-
+
public int describeContents() {
return 0;
}
public void writeToParcel(Parcel dest, int flags) {
+ writeToParcelInternal(dest, flags);
+ }
+
+ /** @hide */
+ public void writeToParcelInternal(Parcel dest, int flags) {
dest.writeInt(mColor);
}
diff --git a/core/java/android/text/style/LeadingMarginSpan.java b/core/java/android/text/style/LeadingMarginSpan.java
index 96a7cd9..339d885 100644
--- a/core/java/android/text/style/LeadingMarginSpan.java
+++ b/core/java/android/text/style/LeadingMarginSpan.java
@@ -125,6 +125,11 @@
}
public int getSpanTypeId() {
+ return getSpanTypeIdInternal();
+ }
+
+ /** @hide */
+ public int getSpanTypeIdInternal() {
return TextUtils.LEADING_MARGIN_SPAN;
}
@@ -133,6 +138,11 @@
}
public void writeToParcel(Parcel dest, int flags) {
+ writeToParcelInternal(dest, flags);
+ }
+
+ /** @hide */
+ public void writeToParcelInternal(Parcel dest, int flags) {
dest.writeInt(mFirst);
dest.writeInt(mRest);
}
diff --git a/core/java/android/text/style/LocaleSpan.java b/core/java/android/text/style/LocaleSpan.java
index a12c42f..d286231 100644
--- a/core/java/android/text/style/LocaleSpan.java
+++ b/core/java/android/text/style/LocaleSpan.java
@@ -44,6 +44,11 @@
@Override
public int getSpanTypeId() {
+ return getSpanTypeIdInternal();
+ }
+
+ /** @hide */
+ public int getSpanTypeIdInternal() {
return TextUtils.LOCALE_SPAN;
}
@@ -54,6 +59,11 @@
@Override
public void writeToParcel(Parcel dest, int flags) {
+ writeToParcelInternal(dest, flags);
+ }
+
+ /** @hide */
+ public void writeToParcelInternal(Parcel dest, int flags) {
dest.writeString(mLocale.getLanguage());
dest.writeString(mLocale.getCountry());
dest.writeString(mLocale.getVariant());
diff --git a/core/java/android/text/style/QuoteSpan.java b/core/java/android/text/style/QuoteSpan.java
index 17748ca..0b0a82c 100644
--- a/core/java/android/text/style/QuoteSpan.java
+++ b/core/java/android/text/style/QuoteSpan.java
@@ -45,6 +45,11 @@
}
public int getSpanTypeId() {
+ return getSpanTypeIdInternal();
+ }
+
+ /** @hide */
+ public int getSpanTypeIdInternal() {
return TextUtils.QUOTE_SPAN;
}
@@ -53,6 +58,11 @@
}
public void writeToParcel(Parcel dest, int flags) {
+ writeToParcelInternal(dest, flags);
+ }
+
+ /** @hide */
+ public void writeToParcelInternal(Parcel dest, int flags) {
dest.writeInt(mColor);
}
diff --git a/core/java/android/text/style/RelativeSizeSpan.java b/core/java/android/text/style/RelativeSizeSpan.java
index 632dbd4..95f048a 100644
--- a/core/java/android/text/style/RelativeSizeSpan.java
+++ b/core/java/android/text/style/RelativeSizeSpan.java
@@ -34,6 +34,11 @@
}
public int getSpanTypeId() {
+ return getSpanTypeIdInternal();
+ }
+
+ /** @hide */
+ public int getSpanTypeIdInternal() {
return TextUtils.RELATIVE_SIZE_SPAN;
}
@@ -42,6 +47,11 @@
}
public void writeToParcel(Parcel dest, int flags) {
+ writeToParcelInternal(dest, flags);
+ }
+
+ /** @hide */
+ public void writeToParcelInternal(Parcel dest, int flags) {
dest.writeFloat(mProportion);
}
diff --git a/core/java/android/text/style/ScaleXSpan.java b/core/java/android/text/style/ScaleXSpan.java
index a22a5a1..d0850185 100644
--- a/core/java/android/text/style/ScaleXSpan.java
+++ b/core/java/android/text/style/ScaleXSpan.java
@@ -34,6 +34,11 @@
}
public int getSpanTypeId() {
+ return getSpanTypeIdInternal();
+ }
+
+ /** @hide */
+ public int getSpanTypeIdInternal() {
return TextUtils.SCALE_X_SPAN;
}
@@ -42,6 +47,11 @@
}
public void writeToParcel(Parcel dest, int flags) {
+ writeToParcelInternal(dest, flags);
+ }
+
+ /** @hide */
+ public void writeToParcelInternal(Parcel dest, int flags) {
dest.writeFloat(mProportion);
}
diff --git a/core/java/android/text/style/SpellCheckSpan.java b/core/java/android/text/style/SpellCheckSpan.java
index 0d8a103..10275c2 100644
--- a/core/java/android/text/style/SpellCheckSpan.java
+++ b/core/java/android/text/style/SpellCheckSpan.java
@@ -54,11 +54,21 @@
@Override
public void writeToParcel(Parcel dest, int flags) {
+ writeToParcelInternal(dest, flags);
+ }
+
+ /** @hide */
+ public void writeToParcelInternal(Parcel dest, int flags) {
dest.writeInt(mSpellCheckInProgress ? 1 : 0);
}
@Override
public int getSpanTypeId() {
+ return getSpanTypeIdInternal();
+ }
+
+ /** @hide */
+ public int getSpanTypeIdInternal() {
return TextUtils.SPELL_CHECK_SPAN;
}
}
diff --git a/core/java/android/text/style/StrikethroughSpan.java b/core/java/android/text/style/StrikethroughSpan.java
index 303e41574..1389704 100644
--- a/core/java/android/text/style/StrikethroughSpan.java
+++ b/core/java/android/text/style/StrikethroughSpan.java
@@ -30,6 +30,11 @@
}
public int getSpanTypeId() {
+ return getSpanTypeIdInternal();
+ }
+
+ /** @hide */
+ public int getSpanTypeIdInternal() {
return TextUtils.STRIKETHROUGH_SPAN;
}
@@ -38,6 +43,11 @@
}
public void writeToParcel(Parcel dest, int flags) {
+ writeToParcelInternal(dest, flags);
+ }
+
+ /** @hide */
+ public void writeToParcelInternal(Parcel dest, int flags) {
}
@Override
diff --git a/core/java/android/text/style/StyleSpan.java b/core/java/android/text/style/StyleSpan.java
index b08f70e..f900db5 100644
--- a/core/java/android/text/style/StyleSpan.java
+++ b/core/java/android/text/style/StyleSpan.java
@@ -50,6 +50,11 @@
}
public int getSpanTypeId() {
+ return getSpanTypeIdInternal();
+ }
+
+ /** @hide */
+ public int getSpanTypeIdInternal() {
return TextUtils.STYLE_SPAN;
}
@@ -58,6 +63,11 @@
}
public void writeToParcel(Parcel dest, int flags) {
+ writeToParcelInternal(dest, flags);
+ }
+
+ /** @hide */
+ public void writeToParcelInternal(Parcel dest, int flags) {
dest.writeInt(mStyle);
}
diff --git a/core/java/android/text/style/SubscriptSpan.java b/core/java/android/text/style/SubscriptSpan.java
index de1d8b2..f1b0d38 100644
--- a/core/java/android/text/style/SubscriptSpan.java
+++ b/core/java/android/text/style/SubscriptSpan.java
@@ -29,6 +29,11 @@
}
public int getSpanTypeId() {
+ return getSpanTypeIdInternal();
+ }
+
+ /** @hide */
+ public int getSpanTypeIdInternal() {
return TextUtils.SUBSCRIPT_SPAN;
}
@@ -37,6 +42,11 @@
}
public void writeToParcel(Parcel dest, int flags) {
+ writeToParcelInternal(dest, flags);
+ }
+
+ /** @hide */
+ public void writeToParcelInternal(Parcel dest, int flags) {
}
@Override
diff --git a/core/java/android/text/style/SuggestionRangeSpan.java b/core/java/android/text/style/SuggestionRangeSpan.java
index 2dbfc72..c1943d5 100644
--- a/core/java/android/text/style/SuggestionRangeSpan.java
+++ b/core/java/android/text/style/SuggestionRangeSpan.java
@@ -46,11 +46,21 @@
@Override
public void writeToParcel(Parcel dest, int flags) {
+ writeToParcelInternal(dest, flags);
+ }
+
+ /** @hide */
+ public void writeToParcelInternal(Parcel dest, int flags) {
dest.writeInt(mBackgroundColor);
}
@Override
public int getSpanTypeId() {
+ return getSpanTypeIdInternal();
+ }
+
+ /** @hide */
+ public int getSpanTypeIdInternal() {
return TextUtils.SUGGESTION_RANGE_SPAN;
}
diff --git a/core/java/android/text/style/SuggestionSpan.java b/core/java/android/text/style/SuggestionSpan.java
index 8b40953..6b449f9 100644
--- a/core/java/android/text/style/SuggestionSpan.java
+++ b/core/java/android/text/style/SuggestionSpan.java
@@ -248,6 +248,11 @@
@Override
public void writeToParcel(Parcel dest, int flags) {
+ writeToParcelInternal(dest, flags);
+ }
+
+ /** @hide */
+ public void writeToParcelInternal(Parcel dest, int flags) {
dest.writeStringArray(mSuggestions);
dest.writeInt(mFlags);
dest.writeString(mLocaleString);
@@ -264,6 +269,11 @@
@Override
public int getSpanTypeId() {
+ return getSpanTypeIdInternal();
+ }
+
+ /** @hide */
+ public int getSpanTypeIdInternal() {
return TextUtils.SUGGESTION_SPAN;
}
diff --git a/core/java/android/text/style/SuperscriptSpan.java b/core/java/android/text/style/SuperscriptSpan.java
index 285fe84..abcf688 100644
--- a/core/java/android/text/style/SuperscriptSpan.java
+++ b/core/java/android/text/style/SuperscriptSpan.java
@@ -29,6 +29,11 @@
}
public int getSpanTypeId() {
+ return getSpanTypeIdInternal();
+ }
+
+ /** @hide */
+ public int getSpanTypeIdInternal() {
return TextUtils.SUPERSCRIPT_SPAN;
}
@@ -37,6 +42,11 @@
}
public void writeToParcel(Parcel dest, int flags) {
+ writeToParcelInternal(dest, flags);
+ }
+
+ /** @hide */
+ public void writeToParcelInternal(Parcel dest, int flags) {
}
@Override
diff --git a/core/java/android/text/style/TextAppearanceSpan.java b/core/java/android/text/style/TextAppearanceSpan.java
index ecbf4bc..abbd793 100644
--- a/core/java/android/text/style/TextAppearanceSpan.java
+++ b/core/java/android/text/style/TextAppearanceSpan.java
@@ -136,6 +136,11 @@
}
public int getSpanTypeId() {
+ return getSpanTypeIdInternal();
+ }
+
+ /** @hide */
+ public int getSpanTypeIdInternal() {
return TextUtils.TEXT_APPEARANCE_SPAN;
}
@@ -144,6 +149,11 @@
}
public void writeToParcel(Parcel dest, int flags) {
+ writeToParcelInternal(dest, flags);
+ }
+
+ /** @hide */
+ public void writeToParcelInternal(Parcel dest, int flags) {
dest.writeString(mTypeface);
dest.writeInt(mStyle);
dest.writeInt(mTextSize);
diff --git a/core/java/android/text/style/TtsSpan.java b/core/java/android/text/style/TtsSpan.java
index 342a183..c40f11f 100644
--- a/core/java/android/text/style/TtsSpan.java
+++ b/core/java/android/text/style/TtsSpan.java
@@ -495,12 +495,22 @@
@Override
public void writeToParcel(Parcel dest, int flags) {
+ writeToParcelInternal(dest, flags);
+ }
+
+ /** @hide */
+ public void writeToParcelInternal(Parcel dest, int flags) {
dest.writeString(mType);
dest.writePersistableBundle(mArgs);
}
@Override
public int getSpanTypeId() {
+ return getSpanTypeIdInternal();
+ }
+
+ /** @hide */
+ public int getSpanTypeIdInternal() {
return TextUtils.TTS_SPAN;
}
diff --git a/core/java/android/text/style/TypefaceSpan.java b/core/java/android/text/style/TypefaceSpan.java
index f194060..aa622d8 100644
--- a/core/java/android/text/style/TypefaceSpan.java
+++ b/core/java/android/text/style/TypefaceSpan.java
@@ -42,6 +42,11 @@
}
public int getSpanTypeId() {
+ return getSpanTypeIdInternal();
+ }
+
+ /** @hide */
+ public int getSpanTypeIdInternal() {
return TextUtils.TYPEFACE_SPAN;
}
@@ -50,6 +55,11 @@
}
public void writeToParcel(Parcel dest, int flags) {
+ writeToParcelInternal(dest, flags);
+ }
+
+ /** @hide */
+ public void writeToParcelInternal(Parcel dest, int flags) {
dest.writeString(mFamily);
}
diff --git a/core/java/android/text/style/URLSpan.java b/core/java/android/text/style/URLSpan.java
index 0669b6f..58239ef 100644
--- a/core/java/android/text/style/URLSpan.java
+++ b/core/java/android/text/style/URLSpan.java
@@ -40,6 +40,11 @@
}
public int getSpanTypeId() {
+ return getSpanTypeIdInternal();
+ }
+
+ /** @hide */
+ public int getSpanTypeIdInternal() {
return TextUtils.URL_SPAN;
}
@@ -48,6 +53,11 @@
}
public void writeToParcel(Parcel dest, int flags) {
+ writeToParcelInternal(dest, flags);
+ }
+
+ /** @hide */
+ public void writeToParcelInternal(Parcel dest, int flags) {
dest.writeString(mURL);
}
diff --git a/core/java/android/text/style/UnderlineSpan.java b/core/java/android/text/style/UnderlineSpan.java
index 80b2427..9024dcd 100644
--- a/core/java/android/text/style/UnderlineSpan.java
+++ b/core/java/android/text/style/UnderlineSpan.java
@@ -30,6 +30,11 @@
}
public int getSpanTypeId() {
+ return getSpanTypeIdInternal();
+ }
+
+ /** @hide */
+ public int getSpanTypeIdInternal() {
return TextUtils.UNDERLINE_SPAN;
}
@@ -38,6 +43,11 @@
}
public void writeToParcel(Parcel dest, int flags) {
+ writeToParcelInternal(dest, flags);
+ }
+
+ /** @hide */
+ public void writeToParcelInternal(Parcel dest, int flags) {
}
@Override
diff --git a/core/java/android/view/AccessibilityInteractionController.java b/core/java/android/view/AccessibilityInteractionController.java
index 6e2d110..664c02a 100644
--- a/core/java/android/view/AccessibilityInteractionController.java
+++ b/core/java/android/view/AccessibilityInteractionController.java
@@ -19,6 +19,7 @@
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.Region;
+import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
@@ -158,7 +159,9 @@
try {
mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
applyAppScaleAndMagnificationSpecIfNeeded(infos, spec);
- if (spec != null) {
+ // Recycle if called from another process. Specs are cached in the
+ // system process and obtained from a pool when read from parcel.
+ if (spec != null && android.os.Process.myPid() != Binder.getCallingPid()) {
spec.recycle();
}
adjustIsVisibleToUserIfNeeded(infos, interactiveRegion);
@@ -167,6 +170,12 @@
} catch (RemoteException re) {
/* ignore - the other side will time out */
}
+
+ // Recycle if called from the same process. Regions are obtained in
+ // the system process and instantiated when read from parcel.
+ if (interactiveRegion != null && android.os.Process.myPid() == Binder.getCallingPid()) {
+ interactiveRegion.recycle();
+ }
}
}
@@ -244,7 +253,9 @@
try {
mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
applyAppScaleAndMagnificationSpecIfNeeded(infos, spec);
- if (spec != null) {
+ // Recycle if called from another process. Specs are cached in the
+ // system process and obtained from a pool when read from parcel.
+ if (spec != null && android.os.Process.myPid() != Binder.getCallingPid()) {
spec.recycle();
}
adjustIsVisibleToUserIfNeeded(infos, interactiveRegion);
@@ -252,6 +263,12 @@
} catch (RemoteException re) {
/* ignore - the other side will time out */
}
+
+ // Recycle if called from the same process. Regions are obtained in
+ // the system process and instantiated when read from parcel.
+ if (interactiveRegion != null && android.os.Process.myPid() == Binder.getCallingPid()) {
+ interactiveRegion.recycle();
+ }
}
}
@@ -354,7 +371,9 @@
try {
mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
applyAppScaleAndMagnificationSpecIfNeeded(infos, spec);
- if (spec != null) {
+ // Recycle if called from another process. Specs are cached in the
+ // system process and obtained from a pool when read from parcel.
+ if (spec != null && android.os.Process.myPid() != Binder.getCallingPid()) {
spec.recycle();
}
adjustIsVisibleToUserIfNeeded(infos, interactiveRegion);
@@ -362,6 +381,12 @@
} catch (RemoteException re) {
/* ignore - the other side will time out */
}
+
+ // Recycle if called from the same process. Regions are obtained in
+ // the system process and instantiated when read from parcel.
+ if (interactiveRegion != null && android.os.Process.myPid() == Binder.getCallingPid()) {
+ interactiveRegion.recycle();
+ }
}
}
@@ -468,7 +493,9 @@
try {
mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
applyAppScaleAndMagnificationSpecIfNeeded(focused, spec);
- if (spec != null) {
+ // Recycle if called from another process. Specs are cached in the
+ // system process and obtained from a pool when read from parcel.
+ if (spec != null && android.os.Process.myPid() != Binder.getCallingPid()) {
spec.recycle();
}
adjustIsVisibleToUserIfNeeded(focused, interactiveRegion);
@@ -476,6 +503,12 @@
} catch (RemoteException re) {
/* ignore - the other side will time out */
}
+
+ // Recycle if called from the same process. Regions are obtained in
+ // the system process and instantiated when read from parcel.
+ if (interactiveRegion != null && android.os.Process.myPid() == Binder.getCallingPid()) {
+ interactiveRegion.recycle();
+ }
}
}
@@ -545,7 +578,9 @@
try {
mViewRootImpl.mAttachInfo.mAccessibilityFetchFlags = 0;
applyAppScaleAndMagnificationSpecIfNeeded(next, spec);
- if (spec != null) {
+ // Recycle if called from another process. Specs are cached in the
+ // system process and obtained from a pool when read from parcel.
+ if (spec != null && android.os.Process.myPid() != Binder.getCallingPid()) {
spec.recycle();
}
adjustIsVisibleToUserIfNeeded(next, interactiveRegion);
@@ -553,6 +588,12 @@
} catch (RemoteException re) {
/* ignore - the other side will time out */
}
+
+ // Recycle if called from the same process. Regions are obtained in
+ // the system process and instantiated when read from parcel.
+ if (interactiveRegion != null && android.os.Process.myPid() == Binder.getCallingPid()) {
+ interactiveRegion.recycle();
+ }
}
}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 774307f..126540f 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -17115,6 +17115,7 @@
* Also calls {@link StateListAnimator#jumpToCurrentState()} if there is a StateListAnimator
* attached to this view.
*/
+ @CallSuper
public void jumpDrawablesToCurrentState() {
if (mBackground != null) {
mBackground.jumpToCurrentState();
@@ -17564,13 +17565,16 @@
* @see Drawable#setTintMode(PorterDuff.Mode)
*/
public void setForegroundTintMode(@Nullable PorterDuff.Mode tintMode) {
- if (mBackgroundTint == null) {
- mBackgroundTint = new TintInfo();
+ if (mForegroundInfo == null) {
+ mForegroundInfo = new ForegroundInfo();
}
- mBackgroundTint.mTintMode = tintMode;
- mBackgroundTint.mHasTintMode = true;
+ if (mForegroundInfo.mTintInfo == null) {
+ mForegroundInfo.mTintInfo = new TintInfo();
+ }
+ mForegroundInfo.mTintInfo.mTintMode = tintMode;
+ mForegroundInfo.mTintInfo.mHasTintMode = true;
- applyBackgroundTint();
+ applyForegroundTint();
}
/**
@@ -17580,7 +17584,7 @@
* @return the blending mode used to apply the tint to the foreground
* drawable
* @attr ref android.R.styleable#View_foregroundTintMode
- * @see #setBackgroundTintMode(PorterDuff.Mode)
+ * @see #setForegroundTintMode(PorterDuff.Mode)
*/
@Nullable
public PorterDuff.Mode getForegroundTintMode() {
diff --git a/core/java/android/view/ViewStructure.java b/core/java/android/view/ViewStructure.java
index 886547a..e525474 100644
--- a/core/java/android/view/ViewStructure.java
+++ b/core/java/android/view/ViewStructure.java
@@ -18,7 +18,6 @@
import android.graphics.Rect;
import android.os.Bundle;
-import android.text.TextPaint;
/**
* Container for storing additional per-view data generated by {@link View#onProvideStructure
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index f7b6405..ca5f5ad 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -1058,6 +1058,13 @@
public boolean isKeyguardSecure();
/**
+ * Return whether the keyguard is on.
+ *
+ * @return true if in keyguard is on.
+ */
+ public boolean isKeyguardShowingOrOccluded();
+
+ /**
* inKeyguardRestrictedKeyInputMode
*
* if keyguard screen is showing or in restricted key input mode (i.e. in
diff --git a/core/java/android/widget/ArrayAdapter.java b/core/java/android/widget/ArrayAdapter.java
index 260854f..027f6d6 100644
--- a/core/java/android/widget/ArrayAdapter.java
+++ b/core/java/android/widget/ArrayAdapter.java
@@ -52,12 +52,6 @@
*/
public class ArrayAdapter<T> extends BaseAdapter implements Filterable, ThemedSpinnerAdapter {
/**
- * Contains the list of objects that represent the data of this ArrayAdapter.
- * The content of this list is referred to as "the array" in the documentation.
- */
- private List<T> mObjects;
-
- /**
* Lock used to modify the content of {@link #mObjects}. Any write operation
* performed on the array should be synchronized on this lock. This lock is also
* used by the filter (see {@link #getFilter()} to make a synchronized copy of
@@ -65,6 +59,14 @@
*/
private final Object mLock = new Object();
+ private final LayoutInflater mInflater;
+
+ /**
+ * Contains the list of objects that represent the data of this ArrayAdapter.
+ * The content of this list is referred to as "the array" in the documentation.
+ */
+ private List<T> mObjects;
+
/**
* The resource indicating what views to inflate to display the content of this
* array adapter.
@@ -97,8 +99,6 @@
private ArrayList<T> mOriginalValues;
private ArrayFilter mFilter;
- private LayoutInflater mInflater;
-
/** Layout inflater used for {@link #getDropDownView(int, View, ViewGroup)}. */
private LayoutInflater mDropDownInflater;
@@ -442,9 +442,6 @@
return mDropDownInflater == null ? null : mDropDownInflater.getContext().getTheme();
}
- /**
- * {@inheritDoc}
- */
@Override
public View getDropDownView(int position, View convertView, ViewGroup parent) {
final LayoutInflater inflater = mDropDownInflater == null ? mInflater : mDropDownInflater;
diff --git a/core/java/android/widget/DatePickerCalendarDelegate.java b/core/java/android/widget/DatePickerCalendarDelegate.java
index d38a225..7e542c9 100755
--- a/core/java/android/widget/DatePickerCalendarDelegate.java
+++ b/core/java/android/widget/DatePickerCalendarDelegate.java
@@ -115,7 +115,8 @@
R.styleable.DatePicker_internalLayout, R.layout.date_picker_material);
// Set up and attach container.
- mContainer = (ViewGroup) inflater.inflate(layoutResourceId, mDelegator);
+ mContainer = (ViewGroup) inflater.inflate(layoutResourceId, mDelegator, false);
+ mDelegator.addView(mContainer);
// Set up header views.
final ViewGroup header = (ViewGroup) mContainer.findViewById(R.id.date_picker_header);
@@ -471,7 +472,11 @@
@Override
public void setEnabled(boolean enabled) {
- mContainer.setEnabled(false);
+ mContainer.setEnabled(enabled);
+ mDayPickerView.setEnabled(enabled);
+ mYearPickerView.setEnabled(enabled);
+ mHeaderYear.setEnabled(enabled);
+ mHeaderMonthDay.setEnabled(enabled);
}
@Override
@@ -481,8 +486,7 @@
@Override
public CalendarView getCalendarView() {
- throw new UnsupportedOperationException(
- "CalendarView does not exists for the new DatePicker");
+ throw new UnsupportedOperationException("Not supported by calendar-mode DatePicker");
}
@Override
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 0cac529..56f9b5c 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -1004,14 +1004,14 @@
stopSelectionActionMode();
} else {
stopSelectionActionMode();
- startSelectionActionModeWithSelectionAndStartDrag();
+ selectCurrentWordAndStartDrag();
}
handled = true;
}
// Start a new selection
if (!handled) {
- handled = startSelectionActionModeWithSelectionAndStartDrag();
+ handled = selectCurrentWordAndStartDrag();
}
return handled;
@@ -1724,22 +1724,9 @@
}
/**
- * Starts a Selection Action Mode with the current selection and enters drag mode. This should
- * be used whenever the mode is started from a touch event.
- *
- * @return true if the selection mode was actually started.
- */
- private boolean startSelectionActionModeWithSelectionAndStartDrag() {
- boolean selectionStarted = startSelectionActionModeWithSelectionInternal();
- if (selectionStarted) {
- getSelectionController().enterDrag();
- }
- return selectionStarted;
- }
-
- /**
* Starts a Selection Action Mode with the current selection and ensures the selection handles
- * are shown. This should be used when the mode is started from a non-touch event.
+ * are shown if there is a selection, otherwise the insertion handle is shown. This should be
+ * used when the mode is started from a non-touch event.
*
* @return true if the selection mode was actually started.
*/
@@ -1747,40 +1734,67 @@
boolean selectionStarted = startSelectionActionModeWithSelectionInternal();
if (selectionStarted) {
getSelectionController().show();
+ } else if (getInsertionController() != null) {
+ getInsertionController().show();
}
return selectionStarted;
}
- private boolean startSelectionActionModeWithSelectionInternal() {
+ /**
+ * If the TextView allows text selection, selects the current word when no existing selection
+ * was available and starts a drag.
+ *
+ * @return true if the drag was started.
+ */
+ private boolean selectCurrentWordAndStartDrag() {
if (extractedTextModeWillBeStarted()) {
// Cancel the single tap delayed runnable.
if (mSelectionModeWithoutSelectionRunnable != null) {
mTextView.removeCallbacks(mSelectionModeWithoutSelectionRunnable);
}
+ return false;
+ }
+ if (mSelectionActionMode != null) {
+ mSelectionActionMode.finish();
+ }
+ if (!checkFieldAndSelectCurrentWord()) {
+ return false;
+ }
+ getSelectionController().enterDrag();
+ return true;
+ }
+ /**
+ * Checks whether a selection can be performed on the current TextView and if so selects
+ * the current word.
+ *
+ * @return true if there already was a selection or if the current word was selected.
+ */
+ private boolean checkFieldAndSelectCurrentWord() {
+ if (!mTextView.canSelectText() || !mTextView.requestFocus()) {
+ Log.w(TextView.LOG_TAG,
+ "TextView does not support text selection. Selection cancelled.");
return false;
}
+ if (!mTextView.hasSelection()) {
+ // There may already be a selection on device rotation
+ return selectCurrentWord();
+ }
+ return true;
+ }
+
+ private boolean startSelectionActionModeWithSelectionInternal() {
if (mSelectionActionMode != null) {
// Selection action mode is already started
mSelectionActionMode.invalidate();
return false;
}
- if (!mTextView.canSelectText() || !mTextView.requestFocus()) {
- Log.w(TextView.LOG_TAG,
- "TextView does not support text selection. Action mode cancelled.");
+ if (!checkFieldAndSelectCurrentWord()) {
return false;
}
- if (!mTextView.hasSelection()) {
- // There may already be a selection on device rotation
- if (!selectCurrentWord()) {
- // No word found under cursor or text selection not permitted.
- return false;
- }
- }
-
boolean willExtract = extractedTextModeWillBeStarted();
// Do not start the action mode when extracted text will show up full screen, which would
@@ -1829,12 +1843,18 @@
}
if (selectionStart == selectionEnd) {
// Spans overlap the cursor.
- return true;
+ for (int i = 0; i < suggestionSpans.length; i++) {
+ if (suggestionSpans[i].getSuggestions().length > 0) {
+ return true;
+ }
+ }
+ return false;
}
int minSpanStart = mTextView.getText().length();
int maxSpanEnd = 0;
int unionOfSpansCoveringSelectionStartStart = mTextView.getText().length();
int unionOfSpansCoveringSelectionStartEnd = 0;
+ boolean hasValidSuggestions = false;
for (int i = 0; i < suggestionSpans.length; i++) {
final int spanStart = spannable.getSpanStart(suggestionSpans[i]);
final int spanEnd = spannable.getSpanEnd(suggestionSpans[i]);
@@ -1844,11 +1864,16 @@
// The span doesn't cover the current selection start point.
continue;
}
+ hasValidSuggestions =
+ hasValidSuggestions || suggestionSpans[i].getSuggestions().length > 0;
unionOfSpansCoveringSelectionStartStart =
Math.min(unionOfSpansCoveringSelectionStartStart, spanStart);
unionOfSpansCoveringSelectionStartEnd =
Math.max(unionOfSpansCoveringSelectionStartEnd, spanEnd);
}
+ if (!hasValidSuggestions) {
+ return false;
+ }
if (unionOfSpansCoveringSelectionStartStart >= unionOfSpansCoveringSelectionStartEnd) {
// No spans cover the selection start point.
return false;
@@ -4071,12 +4096,17 @@
offset = getNextCursorOffset(selectionEnd, false);
mTouchWordDelta = 0.0f;
}
- mInWord = !getWordIteratorWithText().isBoundary(offset);
positionAtCursorOffset(offset, false);
}
}
@Override
+ protected void positionAtCursorOffset(int offset, boolean parentScrolled) {
+ super.positionAtCursorOffset(offset, parentScrolled);
+ mInWord = !getWordIteratorWithText().isBoundary(offset);
+ }
+
+ @Override
public boolean onTouchEvent(MotionEvent event) {
boolean superResult = super.onTouchEvent(event);
if (event.getActionMasked() == MotionEvent.ACTION_UP) {
@@ -4193,12 +4223,17 @@
offset = getNextCursorOffset(selectionStart, true);
mTouchWordDelta = 0.0f;
}
- mInWord = !getWordIteratorWithText().isBoundary(offset);
positionAtCursorOffset(offset, false);
}
}
@Override
+ protected void positionAtCursorOffset(int offset, boolean parentScrolled) {
+ super.positionAtCursorOffset(offset, parentScrolled);
+ mInWord = !getWordIteratorWithText().isBoundary(offset);
+ }
+
+ @Override
public boolean onTouchEvent(MotionEvent event) {
boolean superResult = super.onTouchEvent(event);
if (event.getActionMasked() == MotionEvent.ACTION_UP) {
@@ -4377,7 +4412,7 @@
boolean stayedInArea = distanceSquared < doubleTapSlop * doubleTapSlop;
if (stayedInArea && isPositionOnText(eventX, eventY)) {
- startSelectionActionModeWithSelectionAndStartDrag();
+ selectCurrentWordAndStartDrag();
mDiscardNextActionUp = true;
}
}
@@ -4480,6 +4515,7 @@
mEndHandle.showAtLocation(endOffset);
// No longer the first dragging motion, reset.
+ startSelectionActionModeWithSelection();
mDragAcceleratorActive = false;
mStartOffset = -1;
}
diff --git a/core/java/android/widget/NumberPicker.java b/core/java/android/widget/NumberPicker.java
index 16dc26d..e7d9226 100644
--- a/core/java/android/widget/NumberPicker.java
+++ b/core/java/android/widget/NumberPicker.java
@@ -16,6 +16,7 @@
package android.widget;
+import android.annotation.CallSuper;
import android.annotation.IntDef;
import android.annotation.Widget;
import android.content.Context;
@@ -608,7 +609,16 @@
mSolidColor = attributesArray.getColor(R.styleable.NumberPicker_solidColor, 0);
- mSelectionDivider = attributesArray.getDrawable(R.styleable.NumberPicker_selectionDivider);
+ final Drawable selectionDivider = attributesArray.getDrawable(
+ R.styleable.NumberPicker_selectionDivider);
+ if (selectionDivider != null) {
+ selectionDivider.setCallback(this);
+ selectionDivider.setLayoutDirection(getLayoutDirection());
+ if (selectionDivider.isStateful()) {
+ selectionDivider.setState(getDrawableState());
+ }
+ }
+ mSelectionDivider = selectionDivider;
final int defSelectionDividerHeight = (int) TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP, UNSCALED_DEFAULT_SELECTION_DIVIDER_HEIGHT,
@@ -1499,6 +1509,38 @@
removeAllCallbacks();
}
+ @CallSuper
+ @Override
+ protected void drawableStateChanged() {
+ super.drawableStateChanged();
+
+ final int[] state = getDrawableState();
+
+ if (mSelectionDivider != null && mSelectionDivider.isStateful()) {
+ mSelectionDivider.setState(state);
+ }
+ }
+
+ @CallSuper
+ @Override
+ public void jumpDrawablesToCurrentState() {
+ super.jumpDrawablesToCurrentState();
+
+ if (mSelectionDivider != null) {
+ mSelectionDivider.jumpToCurrentState();
+ }
+ }
+
+ /** @hide */
+ @Override
+ public void onResolveDrawables(@ResolvedLayoutDir int layoutDirection) {
+ super.onResolveDrawables(layoutDirection);
+
+ if (mSelectionDivider != null) {
+ mSelectionDivider.setLayoutDirection(layoutDirection);
+ }
+ }
+
@Override
protected void onDraw(Canvas canvas) {
if (!mHasSelectorWheel) {
diff --git a/core/java/android/widget/SimpleAdapter.java b/core/java/android/widget/SimpleAdapter.java
index e7760ee..3bf9485 100644
--- a/core/java/android/widget/SimpleAdapter.java
+++ b/core/java/android/widget/SimpleAdapter.java
@@ -52,6 +52,8 @@
* If no appropriate binding can be found, an {@link IllegalStateException} is thrown.
*/
public class SimpleAdapter extends BaseAdapter implements Filterable, ThemedSpinnerAdapter {
+ private final LayoutInflater mInflater;
+
private int[] mTo;
private String[] mFrom;
private ViewBinder mViewBinder;
@@ -60,7 +62,6 @@
private int mResource;
private int mDropDownResource;
- private LayoutInflater mInflater;
/** Layout inflater used for {@link #getDropDownView(int, View, ViewGroup)}. */
private LayoutInflater mDropDownInflater;
@@ -174,8 +175,8 @@
@Override
public View getDropDownView(int position, View convertView, ViewGroup parent) {
- return createViewFromResource(
- mDropDownInflater, position, convertView, parent, mDropDownResource);
+ final LayoutInflater inflater = mDropDownInflater == null ? mInflater : mDropDownInflater;
+ return createViewFromResource(inflater, position, convertView, parent, mDropDownResource);
}
private void bindView(int position, View view) {
diff --git a/core/java/android/widget/TabHost.java b/core/java/android/widget/TabHost.java
index c521f72..27fa3b9 100644
--- a/core/java/android/widget/TabHost.java
+++ b/core/java/android/widget/TabHost.java
@@ -188,32 +188,9 @@
mLocalActivityManager = activityGroup;
}
-
@Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
- final ViewTreeObserver treeObserver = getViewTreeObserver();
- treeObserver.addOnTouchModeChangeListener(this);
- }
-
- @Override
- protected void onDetachedFromWindow() {
- super.onDetachedFromWindow();
- final ViewTreeObserver treeObserver = getViewTreeObserver();
- treeObserver.removeOnTouchModeChangeListener(this);
- }
-
- /**
- * {@inheritDoc}
- */
public void onTouchModeChanged(boolean isInTouchMode) {
- if (!isInTouchMode) {
- // leaving touch mode.. if nothing has focus, let's give it to
- // the indicator of the current tab
- if (mCurrentView != null && (!mCurrentView.hasFocus() || mCurrentView.isFocused())) {
- mTabWidget.getChildTabViewAt(mCurrentTab).requestFocus();
- }
- }
+ // No longer used, but kept to maintain API compatibility.
}
/**
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index e14e39c..3a85476 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -6680,7 +6680,7 @@
StaticLayout.Builder builder = StaticLayout.Builder.obtain(mHint, 0,
mHint.length(), mTextPaint, hintWidth)
.setAlignment(alignment)
- .setTextDir(mTextDir)
+ .setTextDirection(mTextDir)
.setLineSpacing(mSpacingAdd, mSpacingMult)
.setIncludePad(mIncludePad)
.setBreakStrategy(mBreakStrategy)
@@ -6771,7 +6771,7 @@
StaticLayout.Builder builder = StaticLayout.Builder.obtain(mTransformed,
0, mTransformed.length(), mTextPaint, wantWidth)
.setAlignment(alignment)
- .setTextDir(mTextDir)
+ .setTextDirection(mTextDir)
.setLineSpacing(mSpacingAdd, mSpacingMult)
.setIncludePad(mIncludePad)
.setBreakStrategy(mBreakStrategy)
diff --git a/core/java/com/android/internal/inputmethod/InputMethodSubtypeSwitchingController.java b/core/java/com/android/internal/inputmethod/InputMethodSubtypeSwitchingController.java
index ce94727..e607a3f 100644
--- a/core/java/com/android/internal/inputmethod/InputMethodSubtypeSwitchingController.java
+++ b/core/java/com/android/internal/inputmethod/InputMethodSubtypeSwitchingController.java
@@ -20,6 +20,7 @@
import android.content.pm.PackageManager;
import android.text.TextUtils;
import android.util.Log;
+import android.util.Printer;
import android.util.Slog;
import android.view.inputmethod.InputMethodInfo;
import android.view.inputmethod.InputMethodSubtype;
@@ -314,6 +315,15 @@
}
return null;
}
+
+ protected void dump(final Printer pw, final String prefix) {
+ final int N = mImeSubtypeList.size();
+ for (int i = 0; i < N; ++i) {
+ final int rank = i;
+ final ImeSubtypeListItem item = mImeSubtypeList.get(i);
+ pw.println(prefix + "rank=" + rank + " item=" + item);
+ }
+ }
}
private static class DynamicRotationList {
@@ -388,6 +398,14 @@
}
return null;
}
+
+ protected void dump(final Printer pw, final String prefix) {
+ for (int i = 0; i < mUsageHistoryOfSubtypeListItemIndex.length; ++i) {
+ final int rank = mUsageHistoryOfSubtypeListItemIndex[i];
+ final ImeSubtypeListItem item = mImeSubtypeList.get(i);
+ pw.println(prefix + "rank=" + rank + " item=" + item);
+ }
+ }
}
@VisibleForTesting
@@ -478,6 +496,13 @@
}
return result;
}
+
+ protected void dump(final Printer pw) {
+ pw.println(" mSwitchingAwareRotationList:");
+ mSwitchingAwareRotationList.dump(pw, " ");
+ pw.println(" mSwitchingUnawareRotationList:");
+ mSwitchingUnawareRotationList.dump(pw, " ");
+ }
}
private final InputMethodSettings mSettings;
@@ -526,4 +551,12 @@
return mSubtypeList.getSortedInputMethodAndSubtypeList(
showSubtypes, includingAuxiliarySubtypes, isScreenLocked);
}
+
+ public void dump(final Printer pw) {
+ if (mController != null) {
+ mController.dump(pw);
+ } else {
+ pw.println(" mController=null");
+ }
+ }
}
diff --git a/core/java/com/android/internal/widget/FloatingToolbar.java b/core/java/com/android/internal/widget/FloatingToolbar.java
index e02d706..c77d614 100644
--- a/core/java/com/android/internal/widget/FloatingToolbar.java
+++ b/core/java/com/android/internal/widget/FloatingToolbar.java
@@ -37,6 +37,7 @@
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.Window;
+import android.view.WindowInsets;
import android.view.WindowManager;
import android.view.animation.Animation;
import android.view.animation.AnimationSet;
@@ -83,7 +84,7 @@
private final Rect mContentRect = new Rect();
private Menu mMenu;
- private List<CharSequence> mShowingTitles = new ArrayList<CharSequence>();
+ private List<Object> mShowingMenuItems = new ArrayList<Object>();
private MenuItem.OnMenuItemClickListener mMenuItemClickListener = NO_OP_MENUITEM_CLICK_LISTENER;
private int mSuggestedWidth;
@@ -155,7 +156,7 @@
if (!isCurrentlyShowing(menuItems) || mWidthChanged) {
mPopup.dismiss();
mPopup.layoutMenuItems(menuItems, mMenuItemClickListener, mSuggestedWidth);
- mShowingTitles = getMenuItemTitles(menuItems);
+ mShowingMenuItems = getShowingMenuItemsReferences(menuItems);
}
mPopup.updateCoordinates(mContentRect);
if (!mPopup.isShowing()) {
@@ -210,7 +211,7 @@
* Returns true if this floating toolbar is currently showing the specified menu items.
*/
private boolean isCurrentlyShowing(List<MenuItem> menuItems) {
- return mShowingTitles.equals(getMenuItemTitles(menuItems));
+ return mShowingMenuItems.equals(getShowingMenuItemsReferences(menuItems));
}
/**
@@ -233,12 +234,16 @@
return menuItems;
}
- private List<CharSequence> getMenuItemTitles(List<MenuItem> menuItems) {
- List<CharSequence> titles = new ArrayList<CharSequence>();
+ private List<Object> getShowingMenuItemsReferences(List<MenuItem> menuItems) {
+ List<Object> references = new ArrayList<Object>();
for (MenuItem menuItem : menuItems) {
- titles.add(menuItem.getTitle());
+ if (isIconOnlyMenuItem(menuItem)) {
+ references.add(menuItem.getIcon());
+ } else {
+ references.add(menuItem.getTitle());
+ }
}
- return titles;
+ return references;
}
@@ -323,6 +328,7 @@
}
};
+ private final Rect mViewPort = new Rect();
private final Point mCoords = new Point();
private final Region mTouchableRegion = new Region();
@@ -394,7 +400,7 @@
mMainPanel = new FloatingToolbarMainPanel(mParent.getContext(), mOpenOverflow);
}
List<MenuItem> overflowMenuItems =
- mMainPanel.layoutMenuItems(menuItems, suggestedWidth);
+ mMainPanel.layoutMenuItems(menuItems, getToolbarWidth(suggestedWidth));
mMainPanel.setOnMenuItemClickListener(menuItemClickListener);
if (!overflowMenuItems.isEmpty()) {
if (mOverflowPanel == null) {
@@ -430,7 +436,8 @@
// The "show" animation will make this visible.
mContentContainer.setAlpha(0);
}
- updateOverflowHeight(contentRect.top - (mMarginVertical * 2));
+ refreshViewPort();
+ updateOverflowHeight(contentRect.top - (mMarginVertical * 2) - mViewPort.top);
refreshCoordinatesAndOverflowDirection(contentRect);
preparePopupContent();
mPopupWindow.showAtLocation(mParent, Gravity.NO_GRAVITY, mCoords.x, mCoords.y);
@@ -494,6 +501,7 @@
}
cancelOverflowAnimations();
+ refreshViewPort();
refreshCoordinatesAndOverflowDirection(contentRect);
preparePopupContent();
mPopupWindow.update(mCoords.x, mCoords.y, getWidth(), getHeight());
@@ -521,18 +529,24 @@
}
private void refreshCoordinatesAndOverflowDirection(Rect contentRect) {
+ // NOTE: Ensure that mViewPort has been refreshed before this.
+
int x = contentRect.centerX() - getWidth() / 2;
int y;
- if (contentRect.top > getHeight()) {
+ if (contentRect.top - getHeight() > mViewPort.top) {
y = contentRect.top - getHeight();
mOverflowDirection = FloatingToolbarPopup.OVERFLOW_DIRECTION_UP;
- } else if (contentRect.top > getToolbarHeightWithVerticalMargin()) {
+ } else if (contentRect.top - getToolbarHeightWithVerticalMargin() > mViewPort.top) {
y = contentRect.top - getToolbarHeightWithVerticalMargin();
mOverflowDirection = FloatingToolbarPopup.OVERFLOW_DIRECTION_DOWN;
} else {
y = contentRect.bottom;
mOverflowDirection = FloatingToolbarPopup.OVERFLOW_DIRECTION_DOWN;
}
+
+ // Update x so that the toolbar isn't rendered behind the nav bar in landscape.
+ x = Math.max(0, Math.min(x, mViewPort.right - getWidth()));
+
mCoords.set(x, y);
if (mOverflowPanel != null) {
mOverflowPanel.setOverflowDirection(mOverflowDirection);
@@ -821,6 +835,29 @@
mPopupWindow.setHeight(height + mMarginVertical * 2);
}
+
+ private void refreshViewPort() {
+ mParent.getGlobalVisibleRect(mViewPort);
+ WindowInsets windowInsets = mParent.getRootWindowInsets();
+ mViewPort.set(
+ mViewPort.left + windowInsets.getStableInsetLeft(),
+ mViewPort.top + windowInsets.getStableInsetTop(),
+ mViewPort.right - windowInsets.getStableInsetRight(),
+ mViewPort.bottom - windowInsets.getStableInsetBottom());
+ }
+
+ private int getToolbarWidth(int suggestedWidth) {
+ int width = suggestedWidth;
+ refreshViewPort();
+ int maximumWidth = mViewPort.width() - 2 * mParent.getResources()
+ .getDimensionPixelSize(R.dimen.floating_toolbar_horizontal_margin);
+ if (width <= 0) {
+ width = mParent.getResources()
+ .getDimensionPixelSize(R.dimen.floating_toolbar_preferred_width);
+ }
+ return Math.min(width, maximumWidth);
+ }
+
/**
* Sets the touchable region of this popup to be zero. This means that all touch events on
* this popup will go through to the surface behind it.
@@ -906,12 +943,11 @@
*
* @return The menu items that are not included in this main panel.
*/
- public List<MenuItem> layoutMenuItems(List<MenuItem> menuItems, int suggestedWidth) {
+ public List<MenuItem> layoutMenuItems(List<MenuItem> menuItems, int width) {
Preconditions.checkNotNull(menuItems);
- final int toolbarWidth = getAdjustedToolbarWidth(mContext, suggestedWidth)
- // Reserve space for the "open overflow" button.
- - getEstimatedOpenOverflowButtonWidth(mContext);
+ // Reserve space for the "open overflow" button.
+ final int toolbarWidth = width - getEstimatedOpenOverflowButtonWidth(mContext);
int availableWidth = toolbarWidth;
final LinkedList<MenuItem> remainingMenuItems = new LinkedList<MenuItem>(menuItems);
diff --git a/core/jni/android_os_Trace.cpp b/core/jni/android_os_Trace.cpp
index 52fd111..3fd3b3c 100644
--- a/core/jni/android_os_Trace.cpp
+++ b/core/jni/android_os_Trace.cpp
@@ -111,19 +111,19 @@
"()J",
(void*)android_os_Trace_nativeGetEnabledTags },
{ "nativeTraceCounter",
- "(JLjava/lang/String;I)V",
+ "!(JLjava/lang/String;I)V",
(void*)android_os_Trace_nativeTraceCounter },
{ "nativeTraceBegin",
- "(JLjava/lang/String;)V",
+ "!(JLjava/lang/String;)V",
(void*)android_os_Trace_nativeTraceBegin },
{ "nativeTraceEnd",
- "(J)V",
+ "!(J)V",
(void*)android_os_Trace_nativeTraceEnd },
{ "nativeAsyncTraceBegin",
- "(JLjava/lang/String;I)V",
+ "!(JLjava/lang/String;I)V",
(void*)android_os_Trace_nativeAsyncTraceBegin },
{ "nativeAsyncTraceEnd",
- "(JLjava/lang/String;I)V",
+ "!(JLjava/lang/String;I)V",
(void*)android_os_Trace_nativeAsyncTraceEnd },
{ "nativeSetAppTracingAllowed",
"(Z)V",
diff --git a/core/res/res/drawable/number_picker_divider_material.xml b/core/res/res/drawable/number_picker_divider_material.xml
new file mode 100644
index 0000000..2474be0
--- /dev/null
+++ b/core/res/res/drawable/number_picker_divider_material.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:tint="?attr/colorControlNormal"
+ android:shape="rectangle">
+ <solid android:color="@color/black" />
+</shape>
diff --git a/core/res/res/layout/number_picker_material.xml b/core/res/res/layout/number_picker_material.xml
new file mode 100644
index 0000000..47edec4
--- /dev/null
+++ b/core/res/res/layout/number_picker_material.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<view xmlns:android="http://schemas.android.com/apk/res/android"
+ class="android.widget.NumberPicker$CustomEditText"
+ android:id="@+id/numberpicker_input"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:singleLine="true"
+ android:background="@null"
+ android:textAppearance="@style/TextAppearance.Material.Caption" />
diff --git a/core/res/res/values/styles_material.xml b/core/res/res/values/styles_material.xml
index f7a42fa..7782ed7 100644
--- a/core/res/res/values/styles_material.xml
+++ b/core/res/res/values/styles_material.xml
@@ -630,13 +630,13 @@
</style>
<style name="Widget.Material.NumberPicker" parent="Widget.NumberPicker">
- <item name="internalLayout">@layout/number_picker_with_selector_wheel</item>
+ <item name="internalLayout">@layout/number_picker_material</item>
<item name="solidColor">@color/transparent</item>
- <item name="selectionDivider">@drawable/numberpicker_selection_divider</item>
- <item name="selectionDividerHeight">2dip</item>
- <item name="selectionDividersDistance">48dip</item>
- <item name="internalMinWidth">64dip</item>
- <item name="internalMaxHeight">180dip</item>
+ <item name="selectionDivider">@drawable/number_picker_divider_material</item>
+ <item name="selectionDividerHeight">2dp</item>
+ <item name="selectionDividersDistance">48dp</item>
+ <item name="internalMinWidth">64dp</item>
+ <item name="internalMaxHeight">180dp</item>
<item name="virtualButtonPressedDrawable">?attr/selectableItemBackground</item>
</style>
diff --git a/graphics/java/android/graphics/ImageFormat.java b/graphics/java/android/graphics/ImageFormat.java
index d6f8cca..7aa0aef 100644
--- a/graphics/java/android/graphics/ImageFormat.java
+++ b/graphics/java/android/graphics/ImageFormat.java
@@ -587,10 +587,29 @@
public static final int RAW12 = 0x26;
/**
- * Android dense depth image format.
+ * <p>Android dense depth image format.</p>
*
- * Each pixel is 16 bits, representing a depth ranging measurement from
- * a depth camera or similar sensor.
+ * <p>Each pixel is 16 bits, representing a depth ranging measurement from a depth camera or
+ * similar sensor. The 16-bit sample consists of a confidence value and the actual ranging
+ * measurement.</p>
+ *
+ * <p>The confidence value is an estimate of correctness for this sample. It is encoded in the
+ * 3 most significant bits of the sample, with a value of 0 representing 100% confidence, a
+ * value of 1 representing 0% confidence, a value of 2 representing 1/7, a value of 3
+ * representing 2/7, and so on.</p>
+ *
+ * <p>As an example, the following sample extracts the range and confidence from the first pixel
+ * of a DEPTH16-format {@link android.media.Image}, and converts the confidence to a
+ * floating-point value between 0 and 1.f inclusive, with 1.f representing maximum confidence:
+ *
+ * <pre>
+ * ShortBuffer shortDepthBuffer = img.getPlanes()[0].getBuffer().asShortBuffer();
+ * short depthSample = shortDepthBuffer.get()
+ * short depthRange = (short) (depthSample & 0x1FFF);
+ * short depthConfidence = (short) ((depthSample >> 13) & 0x7);
+ * float depthPercentage = depthConfidence == 0 ? 1.f : (depthConfidence - 1) / 7.f;
+ * </pre>
+ * </p>
*
* <p>This format assumes
* <ul>
@@ -602,19 +621,32 @@
*
* <pre> y_size = stride * height </pre>
*
- * When produced by a camera, the units are millimeters.
+ * When produced by a camera, the units for the range are millimeters.
*/
public static final int DEPTH16 = 0x44363159;
/**
* Android sparse depth point cloud format.
*
- * <p>A variable-length list of 3D points, with each point represented
- * by a triple of floats.</p>
+ * <p>A variable-length list of 3D points plus a confidence value, with each point represented
+ * by four floats; first the X, Y, Z position coordinates, and then the confidence value.</p>
*
- * <p>The number of points is {@code (size of the buffer in bytes) / 12}.
+ * <p>The number of points is {@code (size of the buffer in bytes) / 16}.
*
- * The coordinate system and units depend on the source of the point cloud data.
+ * <p>The coordinate system and units of the position values depend on the source of the point
+ * cloud data. The confidence value is between 0.f and 1.f, inclusive, with 0 representing 0%
+ * confidence and 1.f representing 100% confidence in the measured position values.</p>
+ *
+ * <p>As an example, the following code extracts the first depth point in a DEPTH_POINT_CLOUD
+ * format {@link android.media.Image}:
+ * <pre>
+ * FloatBuffer floatDepthBuffer = img.getPlanes()[0].getBuffer().asFloatBuffer();
+ * float x = floatDepthBuffer.get();
+ * float y = floatDepthBuffer.get();
+ * float z = floatDepthBuffer.get();
+ * float confidence = floatDepthBuffer.get();
+ * </pre>
+ *
*/
public static final int DEPTH_POINT_CLOUD = 0x101;
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreBCWorkaroundProvider.java b/keystore/java/android/security/keystore/AndroidKeyStoreBCWorkaroundProvider.java
index 03be759..aa2b946 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreBCWorkaroundProvider.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreBCWorkaroundProvider.java
@@ -43,13 +43,17 @@
private static final String PACKAGE_NAME = "android.security.keystore";
private static final String KEYSTORE_SECRET_KEY_CLASS_NAME =
PACKAGE_NAME + ".AndroidKeyStoreSecretKey";
+ private static final String KEYSTORE_PRIVATE_KEY_CLASS_NAME =
+ PACKAGE_NAME + ".AndroidKeyStorePrivateKey";
+ private static final String KEYSTORE_PUBLIC_KEY_CLASS_NAME =
+ PACKAGE_NAME + ".AndroidKeyStorePublicKey";
AndroidKeyStoreBCWorkaroundProvider() {
super("AndroidKeyStoreBCWorkaround",
1.0,
"Android KeyStore security provider to work around Bouncy Castle");
- // javax.crypto.Mac
+ // --------------------- javax.crypto.Mac
putMacImpl("HmacSHA1", PACKAGE_NAME + ".AndroidKeyStoreHmacSpi$HmacSHA1");
put("Alg.Alias.Mac.1.2.840.113549.2.7", "HmacSHA1");
put("Alg.Alias.Mac.HMAC-SHA1", "HmacSHA1");
@@ -75,7 +79,7 @@
put("Alg.Alias.Mac.HMAC-SHA512", "HmacSHA512");
put("Alg.Alias.Mac.HMAC/SHA512", "HmacSHA512");
- // javax.crypto.Cipher
+ // --------------------- javax.crypto.Cipher
putSymmetricCipherImpl("AES/ECB/NoPadding",
PACKAGE_NAME + ".AndroidKeyStoreUnauthenticatedAESCipherSpi$ECB$NoPadding");
putSymmetricCipherImpl("AES/ECB/PKCS7Padding",
@@ -88,6 +92,36 @@
putSymmetricCipherImpl("AES/CTR/NoPadding",
PACKAGE_NAME + ".AndroidKeyStoreUnauthenticatedAESCipherSpi$CTR$NoPadding");
+
+ putAsymmetricCipherImpl("RSA/ECB/NoPadding",
+ PACKAGE_NAME + ".AndroidKeyStoreRSACipherSpi$NoPadding");
+ put("Alg.Alias.Cipher.RSA/None/NoPadding", "RSA/ECB/NoPadding");
+ putAsymmetricCipherImpl("RSA/ECB/PKCS1Padding",
+ PACKAGE_NAME + ".AndroidKeyStoreRSACipherSpi$PKCS1Padding");
+ put("Alg.Alias.Cipher.RSA/None/PKCS1Padding", "RSA/ECB/PKCS1Padding");
+ putAsymmetricCipherImpl("RSA/ECB/OAEPPadding",
+ PACKAGE_NAME + ".AndroidKeyStoreRSACipherSpi$OAEPWithSHA1AndMGF1Padding");
+ put("Alg.Alias.Cipher.RSA/None/OAEPPadding", "RSA/ECB/OAEPPadding");
+ putAsymmetricCipherImpl("RSA/ECB/OAEPWithSHA-1AndMGF1Padding",
+ PACKAGE_NAME + ".AndroidKeyStoreRSACipherSpi$OAEPWithSHA1AndMGF1Padding");
+ put("Alg.Alias.Cipher.RSA/None/OAEPWithSHA-1AndMGF1Padding",
+ "RSA/ECB/OAEPWithSHA-1AndMGF1Padding");
+ putAsymmetricCipherImpl("RSA/ECB/OAEPWithSHA-224AndMGF1Padding",
+ PACKAGE_NAME + ".AndroidKeyStoreRSACipherSpi$OAEPWithSHA224AndMGF1Padding");
+ put("Alg.Alias.Cipher.RSA/None/OAEPWithSHA-224AndMGF1Padding",
+ "RSA/ECB/OAEPWithSHA-256AndMGF1Padding");
+ putAsymmetricCipherImpl("RSA/ECB/OAEPWithSHA-256AndMGF1Padding",
+ PACKAGE_NAME + ".AndroidKeyStoreRSACipherSpi$OAEPWithSHA256AndMGF1Padding");
+ put("Alg.Alias.Cipher.RSA/None/OAEPWithSHA-256AndMGF1Padding",
+ "RSA/ECB/OAEPWithSHA-256AndMGF1Padding");
+ putAsymmetricCipherImpl("RSA/ECB/OAEPWithSHA-384AndMGF1Padding",
+ PACKAGE_NAME + ".AndroidKeyStoreRSACipherSpi$OAEPWithSHA384AndMGF1Padding");
+ put("Alg.Alias.Cipher.RSA/None/OAEPWithSHA-384AndMGF1Padding",
+ "RSA/ECB/OAEPWithSHA-384AndMGF1Padding");
+ putAsymmetricCipherImpl("RSA/ECB/OAEPWithSHA-512AndMGF1Padding",
+ PACKAGE_NAME + ".AndroidKeyStoreRSACipherSpi$OAEPWithSHA512AndMGF1Padding");
+ put("Alg.Alias.Cipher.RSA/None/OAEPWithSHA-512AndMGF1Padding",
+ "RSA/ECB/OAEPWithSHA-512AndMGF1Padding");
}
private void putMacImpl(String algorithm, String implClass) {
@@ -99,4 +133,10 @@
put("Cipher." + transformation, implClass);
put("Cipher." + transformation + " SupportedKeyClasses", KEYSTORE_SECRET_KEY_CLASS_NAME);
}
+
+ private void putAsymmetricCipherImpl(String transformation, String implClass) {
+ put("Cipher." + transformation, implClass);
+ put("Cipher." + transformation + " SupportedKeyClasses",
+ KEYSTORE_PRIVATE_KEY_CLASS_NAME + "|" + KEYSTORE_PUBLIC_KEY_CLASS_NAME);
+ }
}
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreCipherSpiBase.java b/keystore/java/android/security/keystore/AndroidKeyStoreCipherSpiBase.java
index 3ad3c9d..fd9bdb8 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreCipherSpiBase.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreCipherSpiBase.java
@@ -66,7 +66,7 @@
*/
private IBinder mOperationToken;
private long mOperationHandle;
- private KeyStoreCryptoOperationChunkedStreamer mMainDataStreamer;
+ private KeyStoreCryptoOperationStreamer mMainDataStreamer;
/**
* Encountered exception which could not be immediately thrown because it was encountered inside
@@ -210,7 +210,6 @@
byte[] additionalEntropy = KeyStoreCryptoOperationUtils.getRandomBytesToMixIntoKeystoreRng(
mRng, getAdditionalEntropyAmountForBegin());
- KeymasterArguments keymasterOutputArgs = new KeymasterArguments();
OperationResult opResult = mKeyStore.begin(
mKey.getAlias(),
mEncrypting ? KeymasterDefs.KM_PURPOSE_ENCRYPT : KeymasterDefs.KM_PURPOSE_DECRYPT,
@@ -247,9 +246,21 @@
}
loadAlgorithmSpecificParametersFromBeginResult(opResult.outParams);
- mMainDataStreamer = new KeyStoreCryptoOperationChunkedStreamer(
+ mMainDataStreamer = createMainDataStreamer(mKeyStore, opResult.token);
+ }
+
+ /**
+ * Creates a streamer which sends plaintext/ciphertext into the provided KeyStore and receives
+ * the corresponding ciphertext/plaintext from the KeyStore.
+ *
+ * <p>This implementation returns a working streamer.
+ */
+ @NonNull
+ protected KeyStoreCryptoOperationStreamer createMainDataStreamer(
+ KeyStore keyStore, IBinder operationToken) {
+ return new KeyStoreCryptoOperationChunkedStreamer(
new KeyStoreCryptoOperationChunkedStreamer.MainDataStream(
- mKeyStore, opResult.token));
+ keyStore, operationToken));
}
@Override
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreKey.java b/keystore/java/android/security/keystore/AndroidKeyStoreKey.java
index 6098e5c..1751aa5 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreKey.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreKey.java
@@ -19,7 +19,7 @@
import java.security.Key;
/**
- * {@link Key} backed by AndroidKeyStore.
+ * {@link Key} backed by Android Keystore.
*
* @hide
*/
diff --git a/keystore/java/android/security/keystore/AndroidKeyStorePrivateKey.java b/keystore/java/android/security/keystore/AndroidKeyStorePrivateKey.java
new file mode 100644
index 0000000..b586ad4
--- /dev/null
+++ b/keystore/java/android/security/keystore/AndroidKeyStorePrivateKey.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.keystore;
+
+import java.security.PrivateKey;
+
+/**
+ * {@link PrivateKey} backed by Android Keystore.
+ *
+ * @hide
+ */
+public class AndroidKeyStorePrivateKey extends AndroidKeyStoreKey implements PrivateKey {
+
+ public AndroidKeyStorePrivateKey(String alias, String algorithm) {
+ super(alias, algorithm);
+ }
+}
diff --git a/keystore/java/android/security/keystore/AndroidKeyStorePublicKey.java b/keystore/java/android/security/keystore/AndroidKeyStorePublicKey.java
new file mode 100644
index 0000000..8133d46
--- /dev/null
+++ b/keystore/java/android/security/keystore/AndroidKeyStorePublicKey.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.keystore;
+
+import java.security.PublicKey;
+
+/**
+ * {@link PublicKey} backed by Android Keystore.
+ *
+ * @hide
+ */
+public class AndroidKeyStorePublicKey extends AndroidKeyStoreKey implements PublicKey {
+
+ private final byte[] mEncoded;
+
+ public AndroidKeyStorePublicKey(String alias, String algorithm, byte[] x509EncodedForm) {
+ super(alias, algorithm);
+ mEncoded = ArrayUtils.cloneIfNotEmpty(x509EncodedForm);
+ }
+
+ @Override
+ public String getFormat() {
+ return "X.509";
+ }
+
+ @Override
+ public byte[] getEncoded() {
+ return ArrayUtils.cloneIfNotEmpty(mEncoded);
+ }
+}
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreRSACipherSpi.java b/keystore/java/android/security/keystore/AndroidKeyStoreRSACipherSpi.java
new file mode 100644
index 0000000..f70c323
--- /dev/null
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreRSACipherSpi.java
@@ -0,0 +1,487 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.keystore;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.IBinder;
+import android.security.KeyStore;
+import android.security.KeyStoreException;
+import android.security.keymaster.KeyCharacteristics;
+import android.security.keymaster.KeymasterArguments;
+import android.security.keymaster.KeymasterDefs;
+
+import libcore.util.EmptyArray;
+
+import java.io.ByteArrayOutputStream;
+import java.security.AlgorithmParameters;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.Key;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.ProviderException;
+import java.security.spec.AlgorithmParameterSpec;
+import java.security.spec.InvalidParameterSpecException;
+import java.security.spec.MGF1ParameterSpec;
+
+import javax.crypto.Cipher;
+import javax.crypto.CipherSpi;
+import javax.crypto.spec.OAEPParameterSpec;
+import javax.crypto.spec.PSource;
+
+/**
+ * Base class for {@link CipherSpi} providing Android KeyStore backed RSA encryption/decryption.
+ *
+ * @hide
+ */
+abstract class AndroidKeyStoreRSACipherSpi extends AndroidKeyStoreCipherSpiBase {
+
+ /**
+ * Raw RSA cipher without any padding.
+ */
+ public static final class NoPadding extends AndroidKeyStoreRSACipherSpi {
+ public NoPadding() {
+ super(KeymasterDefs.KM_PAD_NONE);
+ }
+
+ @Override
+ protected void initAlgorithmSpecificParameters() throws InvalidKeyException {}
+
+ @Override
+ protected void initAlgorithmSpecificParameters(@Nullable AlgorithmParameterSpec params)
+ throws InvalidAlgorithmParameterException {
+ if (params != null) {
+ throw new InvalidAlgorithmParameterException(
+ "Unexpected parameters: " + params + ". No parameters supported");
+ }
+ }
+
+ @Override
+ protected void initAlgorithmSpecificParameters(@Nullable AlgorithmParameters params)
+ throws InvalidAlgorithmParameterException {
+
+ if (params != null) {
+ throw new InvalidAlgorithmParameterException(
+ "Unexpected parameters: " + params + ". No parameters supported");
+ }
+ }
+
+ @Override
+ protected AlgorithmParameters engineGetParameters() {
+ return null;
+ }
+
+ @Override
+ protected final int getAdditionalEntropyAmountForBegin() {
+ return 0;
+ }
+
+ @Override
+ @NonNull
+ protected KeyStoreCryptoOperationStreamer createMainDataStreamer(
+ KeyStore keyStore, IBinder operationToken) {
+ if (isEncrypting()) {
+ // KeyStore's RSA encryption without padding expects the input to be of the same
+ // length as the modulus. We thus have to buffer all input to pad it with leading
+ // zeros.
+ return new ZeroPaddingEncryptionStreamer(
+ super.createMainDataStreamer(keyStore, operationToken),
+ getModulusSizeBytes());
+ } else {
+ return super.createMainDataStreamer(keyStore, operationToken);
+ }
+ }
+
+ /**
+ * Streamer which buffers all plaintext input, then pads it with leading zeros to match
+ * modulus size, and then sends it into KeyStore to obtain ciphertext.
+ */
+ private static class ZeroPaddingEncryptionStreamer
+ implements KeyStoreCryptoOperationStreamer {
+
+ private final KeyStoreCryptoOperationStreamer mDelegate;
+ private final int mModulusSizeBytes;
+ private final ByteArrayOutputStream mInputBuffer = new ByteArrayOutputStream();
+
+ private ZeroPaddingEncryptionStreamer(
+ KeyStoreCryptoOperationStreamer delegate,
+ int modulusSizeBytes) {
+ mDelegate = delegate;
+ mModulusSizeBytes = modulusSizeBytes;
+ }
+
+ @Override
+ public byte[] update(byte[] input, int inputOffset, int inputLength)
+ throws KeyStoreException {
+ if (inputLength > 0) {
+ mInputBuffer.write(input, inputOffset, inputLength);
+ }
+ return EmptyArray.BYTE;
+ }
+
+ @Override
+ public byte[] doFinal(byte[] input, int inputOffset, int inputLength)
+ throws KeyStoreException {
+ if (inputLength > 0) {
+ mInputBuffer.write(input, inputOffset, inputLength);
+ }
+ byte[] bufferedInput = mInputBuffer.toByteArray();
+ mInputBuffer.reset();
+ byte[] paddedInput;
+ if (bufferedInput.length < mModulusSizeBytes) {
+ // Pad input with leading zeros
+ paddedInput = new byte[mModulusSizeBytes];
+ System.arraycopy(
+ bufferedInput, 0,
+ paddedInput,
+ paddedInput.length - bufferedInput.length,
+ bufferedInput.length);
+ } else {
+ // No need to pad input
+ paddedInput = bufferedInput;
+ }
+ return mDelegate.doFinal(paddedInput, 0, paddedInput.length);
+ }
+ }
+ }
+
+ /**
+ * RSA cipher with PKCS#1 v1.5 encryption padding.
+ */
+ public static final class PKCS1Padding extends AndroidKeyStoreRSACipherSpi {
+ public PKCS1Padding() {
+ super(KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_ENCRYPT);
+ }
+
+ @Override
+ protected void initAlgorithmSpecificParameters() throws InvalidKeyException {}
+
+ @Override
+ protected void initAlgorithmSpecificParameters(@Nullable AlgorithmParameterSpec params)
+ throws InvalidAlgorithmParameterException {
+ if (params != null) {
+ throw new InvalidAlgorithmParameterException(
+ "Unexpected parameters: " + params + ". No parameters supported");
+ }
+ }
+
+ @Override
+ protected void initAlgorithmSpecificParameters(@Nullable AlgorithmParameters params)
+ throws InvalidAlgorithmParameterException {
+
+ if (params != null) {
+ throw new InvalidAlgorithmParameterException(
+ "Unexpected parameters: " + params + ". No parameters supported");
+ }
+ }
+
+ @Override
+ protected AlgorithmParameters engineGetParameters() {
+ return null;
+ }
+
+ @Override
+ protected final int getAdditionalEntropyAmountForBegin() {
+ return (isEncrypting()) ? getModulusSizeBytes() : 0;
+ }
+ }
+
+ /**
+ * RSA cipher with OAEP encryption padding. Only SHA-1 based MGF1 is supported as MGF.
+ */
+ abstract static class OAEPWithMGF1Padding extends AndroidKeyStoreRSACipherSpi {
+
+ private static final String MGF_ALGORITGM_MGF1 = "MGF1";
+
+ private int mKeymasterDigest = -1;
+ private int mDigestOutputSizeBytes;
+
+ OAEPWithMGF1Padding(int keymasterDigest) {
+ super(KeymasterDefs.KM_PAD_RSA_OAEP);
+ mKeymasterDigest = keymasterDigest;
+ mDigestOutputSizeBytes =
+ (KeymasterUtils.getDigestOutputSizeBits(keymasterDigest) + 7) / 8;
+ }
+
+ @Override
+ protected final void initAlgorithmSpecificParameters() throws InvalidKeyException {}
+
+ @Override
+ protected final void initAlgorithmSpecificParameters(
+ @Nullable AlgorithmParameterSpec params) throws InvalidAlgorithmParameterException {
+ if (params == null) {
+ return;
+ }
+
+ if (!(params instanceof OAEPParameterSpec)) {
+ throw new InvalidAlgorithmParameterException(
+ "Unsupported parameter spec: " + params
+ + ". Only OAEPParameterSpec supported");
+ }
+ OAEPParameterSpec spec = (OAEPParameterSpec) params;
+ if (!MGF_ALGORITGM_MGF1.equalsIgnoreCase(spec.getMGFAlgorithm())) {
+ throw new InvalidAlgorithmParameterException(
+ "Unsupported MGF: " + spec.getMGFAlgorithm()
+ + ". Only " + MGF_ALGORITGM_MGF1 + " supported");
+ }
+ String jcaDigest = spec.getDigestAlgorithm();
+ int keymasterDigest;
+ try {
+ keymasterDigest = KeyProperties.Digest.toKeymaster(jcaDigest);
+ } catch (IllegalArgumentException e) {
+ throw new InvalidAlgorithmParameterException(
+ "Unsupported digest: " + jcaDigest, e);
+ }
+ switch (keymasterDigest) {
+ case KeymasterDefs.KM_DIGEST_SHA1:
+ case KeymasterDefs.KM_DIGEST_SHA_2_224:
+ case KeymasterDefs.KM_DIGEST_SHA_2_256:
+ case KeymasterDefs.KM_DIGEST_SHA_2_384:
+ case KeymasterDefs.KM_DIGEST_SHA_2_512:
+ // Permitted.
+ break;
+ default:
+ throw new InvalidAlgorithmParameterException(
+ "Unsupported digest: " + jcaDigest);
+ }
+ AlgorithmParameterSpec mgfParams = spec.getMGFParameters();
+ if (mgfParams == null) {
+ throw new InvalidAlgorithmParameterException("MGF parameters must be provided");
+ }
+ // Check whether MGF parameters match the OAEPParameterSpec
+ if (!(mgfParams instanceof MGF1ParameterSpec)) {
+ throw new InvalidAlgorithmParameterException("Unsupported MGF parameters"
+ + ": " + mgfParams + ". Only MGF1ParameterSpec supported");
+ }
+ MGF1ParameterSpec mgfSpec = (MGF1ParameterSpec) mgfParams;
+ String mgf1JcaDigest = mgfSpec.getDigestAlgorithm();
+ if (!KeyProperties.DIGEST_SHA1.equalsIgnoreCase(mgf1JcaDigest)) {
+ throw new InvalidAlgorithmParameterException(
+ "Unsupported MGF1 digest: " + mgf1JcaDigest
+ + ". Only " + KeyProperties.DIGEST_SHA1 + " supported");
+ }
+ PSource pSource = spec.getPSource();
+ if (!(pSource instanceof PSource.PSpecified)) {
+ throw new InvalidAlgorithmParameterException(
+ "Unsupported source of encoding input P: " + pSource
+ + ". Only pSpecifiedEmpty (PSource.PSpecified.DEFAULT) supported");
+ }
+ PSource.PSpecified pSourceSpecified = (PSource.PSpecified) pSource;
+ byte[] pSourceValue = pSourceSpecified.getValue();
+ if ((pSourceValue != null) && (pSourceValue.length > 0)) {
+ throw new InvalidAlgorithmParameterException(
+ "Unsupported source of encoding input P: " + pSource
+ + ". Only pSpecifiedEmpty (PSource.PSpecified.DEFAULT) supported");
+ }
+ mKeymasterDigest = keymasterDigest;
+ mDigestOutputSizeBytes =
+ (KeymasterUtils.getDigestOutputSizeBits(keymasterDigest) + 7) / 8;
+ }
+
+ @Override
+ protected final void initAlgorithmSpecificParameters(@Nullable AlgorithmParameters params)
+ throws InvalidAlgorithmParameterException {
+ if (params == null) {
+ return;
+ }
+
+ OAEPParameterSpec spec;
+ try {
+ spec = params.getParameterSpec(OAEPParameterSpec.class);
+ } catch (InvalidParameterSpecException e) {
+ throw new InvalidAlgorithmParameterException("OAEP parameters required"
+ + ", but not found in parameters: " + params, e);
+ }
+ if (spec == null) {
+ throw new InvalidAlgorithmParameterException("OAEP parameters required"
+ + ", but not provided in parameters: " + params);
+ }
+ initAlgorithmSpecificParameters(spec);
+ }
+
+ @Override
+ protected final AlgorithmParameters engineGetParameters() {
+ OAEPParameterSpec spec =
+ new OAEPParameterSpec(
+ KeyProperties.Digest.fromKeymaster(mKeymasterDigest),
+ MGF_ALGORITGM_MGF1,
+ MGF1ParameterSpec.SHA1,
+ PSource.PSpecified.DEFAULT);
+ try {
+ AlgorithmParameters params = AlgorithmParameters.getInstance("OAEP");
+ params.init(spec);
+ return params;
+ } catch (NoSuchAlgorithmException e) {
+ throw new ProviderException(
+ "Failed to obtain OAEP AlgorithmParameters", e);
+ } catch (InvalidParameterSpecException e) {
+ throw new ProviderException(
+ "Failed to initialize OAEP AlgorithmParameters with an IV",
+ e);
+ }
+ }
+
+ @Override
+ protected final void addAlgorithmSpecificParametersToBegin(
+ KeymasterArguments keymasterArgs) {
+ super.addAlgorithmSpecificParametersToBegin(keymasterArgs);
+ keymasterArgs.addInt(KeymasterDefs.KM_TAG_DIGEST, mKeymasterDigest);
+ }
+
+ @Override
+ protected final void loadAlgorithmSpecificParametersFromBeginResult(
+ @NonNull KeymasterArguments keymasterArgs) {
+ super.loadAlgorithmSpecificParametersFromBeginResult(keymasterArgs);
+ }
+
+ @Override
+ protected final int getAdditionalEntropyAmountForBegin() {
+ return (isEncrypting()) ? mDigestOutputSizeBytes : 0;
+ }
+ }
+
+ public static class OAEPWithSHA1AndMGF1Padding extends OAEPWithMGF1Padding {
+ public OAEPWithSHA1AndMGF1Padding() {
+ super(KeymasterDefs.KM_DIGEST_SHA1);
+ }
+ }
+
+ public static class OAEPWithSHA224AndMGF1Padding extends OAEPWithMGF1Padding {
+ public OAEPWithSHA224AndMGF1Padding() {
+ super(KeymasterDefs.KM_DIGEST_SHA_2_224);
+ }
+ }
+
+ public static class OAEPWithSHA256AndMGF1Padding extends OAEPWithMGF1Padding {
+ public OAEPWithSHA256AndMGF1Padding() {
+ super(KeymasterDefs.KM_DIGEST_SHA_2_256);
+ }
+ }
+
+ public static class OAEPWithSHA384AndMGF1Padding extends OAEPWithMGF1Padding {
+ public OAEPWithSHA384AndMGF1Padding() {
+ super(KeymasterDefs.KM_DIGEST_SHA_2_384);
+ }
+ }
+
+ public static class OAEPWithSHA512AndMGF1Padding extends OAEPWithMGF1Padding {
+ public OAEPWithSHA512AndMGF1Padding() {
+ super(KeymasterDefs.KM_DIGEST_SHA_2_512);
+ }
+ }
+
+ private final int mKeymasterPadding;
+
+ private int mModulusSizeBytes = -1;
+
+ AndroidKeyStoreRSACipherSpi(int keymasterPadding) {
+ mKeymasterPadding = keymasterPadding;
+ }
+
+ @Override
+ protected final void initKey(int opmode, Key key) throws InvalidKeyException {
+ if (key == null) {
+ throw new InvalidKeyException("Unsupported key: null");
+ }
+ if (!KeyProperties.KEY_ALGORITHM_RSA.equalsIgnoreCase(key.getAlgorithm())) {
+ throw new InvalidKeyException("Unsupported key algorithm: " + key.getAlgorithm()
+ + ". Only " + KeyProperties.KEY_ALGORITHM_RSA + " supported");
+ }
+ AndroidKeyStoreKey keystoreKey;
+ if (key instanceof AndroidKeyStorePrivateKey) {
+ keystoreKey = (AndroidKeyStoreKey) key;
+ } else if (key instanceof AndroidKeyStorePublicKey) {
+ keystoreKey = (AndroidKeyStoreKey) key;
+ } else {
+ throw new InvalidKeyException("Unsupported key type: " + key);
+ }
+
+ if (keystoreKey instanceof PrivateKey) {
+ if ((opmode != Cipher.DECRYPT_MODE) && (opmode != Cipher.UNWRAP_MODE)) {
+ throw new InvalidKeyException("Private key cannot be used with opmode: " + opmode
+ + ". Only DECRYPT_MODE and UNWRAP_MODE supported");
+ }
+ } else {
+ if ((opmode != Cipher.ENCRYPT_MODE) && (opmode != Cipher.WRAP_MODE)) {
+ throw new InvalidKeyException("Public key cannot be used with opmode: " + opmode
+ + ". Only ENCRYPT_MODE and WRAP_MODE supported");
+ }
+ }
+
+ KeyCharacteristics keyCharacteristics = new KeyCharacteristics();
+ int errorCode = getKeyStore().getKeyCharacteristics(
+ keystoreKey.getAlias(), null, null, keyCharacteristics);
+ if (errorCode != KeyStore.NO_ERROR) {
+ throw getKeyStore().getInvalidKeyException(keystoreKey.getAlias(), errorCode);
+ }
+ int keySizeBits = keyCharacteristics.getInt(KeymasterDefs.KM_TAG_KEY_SIZE, -1);
+ if (keySizeBits == -1) {
+ throw new InvalidKeyException("Size of key not known");
+ }
+ mModulusSizeBytes = (keySizeBits + 7) / 8;
+
+ setKey(keystoreKey);
+ }
+
+ @Override
+ protected final void resetAll() {
+ mModulusSizeBytes = -1;
+ super.resetAll();
+ }
+
+ @Override
+ protected final void resetWhilePreservingInitState() {
+ super.resetWhilePreservingInitState();
+ }
+
+ @Override
+ protected void addAlgorithmSpecificParametersToBegin(
+ @NonNull KeymasterArguments keymasterArgs) {
+ keymasterArgs.addInt(KeymasterDefs.KM_TAG_ALGORITHM, KeymasterDefs.KM_ALGORITHM_RSA);
+ keymasterArgs.addInt(KeymasterDefs.KM_TAG_PADDING, mKeymasterPadding);
+ }
+
+ @Override
+ protected void loadAlgorithmSpecificParametersFromBeginResult(
+ @NonNull KeymasterArguments keymasterArgs) {
+ }
+
+ @Override
+ protected final int engineGetBlockSize() {
+ // Not a block cipher, according to the RI
+ return 0;
+ }
+
+ @Override
+ protected final byte[] engineGetIV() {
+ // IV never used
+ return null;
+ }
+
+ @Override
+ protected final int engineGetOutputSize(int inputLen) {
+ return getModulusSizeBytes();
+ }
+
+ protected final int getModulusSizeBytes() {
+ if (mModulusSizeBytes == -1) {
+ throw new IllegalStateException("Not initialized");
+ }
+ return mModulusSizeBytes;
+ }
+}
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreRSAPublicKey.java b/keystore/java/android/security/keystore/AndroidKeyStoreRSAPublicKey.java
new file mode 100644
index 0000000..36bc997b
--- /dev/null
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreRSAPublicKey.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.keystore;
+
+import java.math.BigInteger;
+import java.security.interfaces.RSAPublicKey;
+
+/**
+ * {@link RSAPublicKey} backed by Android Keystore.
+ *
+ * @hide
+ */
+public class AndroidKeyStoreRSAPublicKey extends AndroidKeyStorePublicKey implements RSAPublicKey {
+ private final BigInteger mModulus;
+ private final BigInteger mPublicExponent;
+
+ public AndroidKeyStoreRSAPublicKey(String alias, byte[] x509EncodedForm, BigInteger modulus,
+ BigInteger publicExponent) {
+ super(alias, "RSA", x509EncodedForm);
+ mModulus = modulus;
+ mPublicExponent = publicExponent;
+ }
+
+ public AndroidKeyStoreRSAPublicKey(String alias, RSAPublicKey info) {
+ this(alias, info.getEncoded(), info.getModulus(), info.getPublicExponent());
+ if (!"X.509".equalsIgnoreCase(info.getFormat())) {
+ throw new IllegalArgumentException(
+ "Unsupported key export format: " + info.getFormat());
+ }
+ }
+
+ @Override
+ public BigInteger getModulus() {
+ return mModulus;
+ }
+
+ @Override
+ public BigInteger getPublicExponent() {
+ return mPublicExponent;
+ }
+}
diff --git a/keystore/java/android/security/keystore/AndroidKeyStoreSecretKey.java b/keystore/java/android/security/keystore/AndroidKeyStoreSecretKey.java
index f75516b..af354ab 100644
--- a/keystore/java/android/security/keystore/AndroidKeyStoreSecretKey.java
+++ b/keystore/java/android/security/keystore/AndroidKeyStoreSecretKey.java
@@ -19,7 +19,7 @@
import javax.crypto.SecretKey;
/**
- * {@link SecretKey} backed by keystore.
+ * {@link SecretKey} backed by Android Keystore.
*
* @hide
*/
diff --git a/keystore/java/android/security/keystore/KeyStoreCryptoOperationChunkedStreamer.java b/keystore/java/android/security/keystore/KeyStoreCryptoOperationChunkedStreamer.java
index 7d57e5f..47b4996 100644
--- a/keystore/java/android/security/keystore/KeyStoreCryptoOperationChunkedStreamer.java
+++ b/keystore/java/android/security/keystore/KeyStoreCryptoOperationChunkedStreamer.java
@@ -44,12 +44,12 @@
*
* @hide
*/
-public class KeyStoreCryptoOperationChunkedStreamer {
+class KeyStoreCryptoOperationChunkedStreamer implements KeyStoreCryptoOperationStreamer {
/**
* Bidirectional chunked data stream over a KeyStore crypto operation.
*/
- public interface Stream {
+ interface Stream {
/**
* Returns the result of the KeyStore {@code update} operation or null if keystore couldn't
* be reached.
@@ -66,12 +66,11 @@
// Binder buffer is about 1MB, but it's shared between all active transactions of the process.
// Thus, it's safer to use a much smaller upper bound.
private static final int DEFAULT_MAX_CHUNK_SIZE = 64 * 1024;
- private static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
private final Stream mKeyStoreStream;
private final int mMaxChunkSize;
- private byte[] mBuffered = EMPTY_BYTE_ARRAY;
+ private byte[] mBuffered = EmptyArray.BYTE;
private int mBufferedOffset;
private int mBufferedLength;
@@ -84,10 +83,11 @@
mMaxChunkSize = maxChunkSize;
}
+ @Override
public byte[] update(byte[] input, int inputOffset, int inputLength) throws KeyStoreException {
if (inputLength == 0) {
// No input provided
- return EMPTY_BYTE_ARRAY;
+ return EmptyArray.BYTE;
}
ByteArrayOutputStream bufferedOutput = null;
@@ -129,7 +129,7 @@
if (opResult.inputConsumed == chunk.length) {
// The whole chunk was consumed
- mBuffered = EMPTY_BYTE_ARRAY;
+ mBuffered = EmptyArray.BYTE;
mBufferedOffset = 0;
mBufferedLength = 0;
} else if (opResult.inputConsumed == 0) {
@@ -185,17 +185,18 @@
if (bufferedOutput == null) {
// No output produced
- return EMPTY_BYTE_ARRAY;
+ return EmptyArray.BYTE;
} else {
return bufferedOutput.toByteArray();
}
}
+ @Override
public byte[] doFinal(byte[] input, int inputOffset, int inputLength)
throws KeyStoreException {
if (inputLength == 0) {
// No input provided -- simplify the rest of the code
- input = EMPTY_BYTE_ARRAY;
+ input = EmptyArray.BYTE;
inputOffset = 0;
}
diff --git a/keystore/java/android/security/keystore/KeyStoreCryptoOperationStreamer.java b/keystore/java/android/security/keystore/KeyStoreCryptoOperationStreamer.java
new file mode 100644
index 0000000..2fb8f20
--- /dev/null
+++ b/keystore/java/android/security/keystore/KeyStoreCryptoOperationStreamer.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.keystore;
+
+import android.security.KeyStore;
+import android.security.KeyStoreException;
+
+/**
+ * Helper for streaming a crypto operation's input and output via {@link KeyStore} service's
+ * {@code update} and {@code finish} operations.
+ *
+ * <p>The helper abstracts away to issues that need to be solved in most code that uses KeyStore's
+ * update and finish operations. Firstly, KeyStore's update operation can consume only a limited
+ * amount of data in one go because the operations are marshalled via Binder. Secondly, the update
+ * operation may consume less data than provided, in which case the caller has to buffer the
+ * remainder for next time. The helper exposes {@link #update(byte[], int, int) update} and
+ * {@link #doFinal(byte[], int, int) doFinal} operations which can be used to conveniently implement
+ * various JCA crypto primitives.
+ *
+ * @hide
+ */
+interface KeyStoreCryptoOperationStreamer {
+ byte[] update(byte[] input, int inputOffset, int inputLength) throws KeyStoreException;
+ byte[] doFinal(byte[] input, int inputOffset, int inputLength) throws KeyStoreException;
+}
diff --git a/media/java/android/media/ClosedCaptionRenderer.java b/media/java/android/media/ClosedCaptionRenderer.java
index e3680e9..8403c1c 100644
--- a/media/java/android/media/ClosedCaptionRenderer.java
+++ b/media/java/android/media/ClosedCaptionRenderer.java
@@ -1044,42 +1044,26 @@
}
/**
+ * Mutable version of BackgroundSpan to facilitate text rendering with edge
+ * styles.
+ *
* @hide
- *
- * MutableBackgroundColorSpan
- *
- * This is a mutable version of BackgroundSpan to facilitate text
- * rendering with edge styles.
- *
*/
-class MutableBackgroundColorSpan extends CharacterStyle
- implements UpdateAppearance, ParcelableSpan {
+class MutableBackgroundColorSpan extends CharacterStyle implements UpdateAppearance {
private int mColor;
public MutableBackgroundColorSpan(int color) {
mColor = color;
}
- public MutableBackgroundColorSpan(Parcel src) {
- mColor = src.readInt();
- }
+
public void setBackgroundColor(int color) {
mColor = color;
}
+
public int getBackgroundColor() {
return mColor;
}
- @Override
- public int getSpanTypeId() {
- return TextUtils.BACKGROUND_COLOR_SPAN;
- }
- @Override
- public int describeContents() {
- return 0;
- }
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeInt(mColor);
- }
+
@Override
public void updateDrawState(TextPaint ds) {
ds.bgColor = mColor;
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java
index 29c3c75..cd0e587 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/CameraMetadataTest.java
@@ -731,27 +731,31 @@
@SmallTest
public void testReadWriteHighSpeedVideoConfiguration() {
- // int32 x 4 x 1
+ // int32 x 5 x 1
checkKeyMarshal("android.control.availableHighSpeedVideoConfigurations",
new HighSpeedVideoConfiguration(
- /*width*/1000, /*height*/255, /*fpsMin*/30, /*fpsMax*/200),
+ /*width*/1000, /*height*/255, /*fpsMin*/30, /*fpsMax*/200,
+ /*batchSizeMax*/8),
/* width, height, fpsMin, fpsMax */
- toByteArray(1000, 255, 30, 200));
+ toByteArray(1000, 255, 30, 200, 8));
- // int32 x 4 x 3
+ // int32 x 5 x 3
checkKeyMarshal("android.control.availableHighSpeedVideoConfigurations",
new HighSpeedVideoConfiguration[] {
new HighSpeedVideoConfiguration(
- /*width*/1280, /*height*/720, /*fpsMin*/60, /*fpsMax*/120),
+ /*width*/1280, /*height*/720, /*fpsMin*/60, /*fpsMax*/120,
+ /*batchSizeMax*/8),
new HighSpeedVideoConfiguration(
- /*width*/123, /*height*/456, /*fpsMin*/1, /*fpsMax*/200),
+ /*width*/123, /*height*/456, /*fpsMin*/1, /*fpsMax*/200,
+ /*batchSizeMax*/4),
new HighSpeedVideoConfiguration(
- /*width*/4096, /*height*/2592, /*fpsMin*/30, /*fpsMax*/60)
+ /*width*/4096, /*height*/2592, /*fpsMin*/30, /*fpsMax*/60,
+ /*batchSizeMax*/2)
},
toByteArray(
- 1280, 720, 60, 120,
- 123, 456, 1, 200,
- 4096, 2592, 30, 60
+ 1280, 720, 60, 120, 8,
+ 123, 456, 1, 200, 4,
+ 4096, 2592, 30, 60, 2
));
}
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index fd0ba73..640fb29 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -25,6 +25,7 @@
<!-- Standard permissions granted to the shell. -->
<uses-permission android:name="android.permission.SEND_SMS" />
<uses-permission android:name="android.permission.CALL_PHONE" />
+ <uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.WRITE_CONTACTS" />
<uses-permission android:name="android.permission.READ_CALENDAR" />
@@ -97,6 +98,10 @@
<uses-permission android:name="android.permission.CHANGE_APP_IDLE_STATE" />
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
<uses-permission android:name="android.permission.MOUNT_FORMAT_FILESYSTEMS" />
+ <uses-permission android:name="android.permission.MODIFY_PHONE_STATE" />
+ <uses-permission android:name="android.permission.REGISTER_CALL_PROVIDER" />
+ <uses-permission android:name="android.permission.REGISTER_CONNECTION_MANAGER" />
+ <uses-permission android:name="android.permission.REGISTER_SIM_SUBSCRIPTION" />
<application android:label="@string/app_label">
<provider
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 18a19cb..9f38185 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -336,7 +336,7 @@
<dimen name="keyguard_affordance_min_background_radius">30dp</dimen>
<!-- The size of the touch targets on the keyguard for the affordances. -->
- <dimen name="keyguard_affordance_touch_target_size">96dp</dimen>
+ <dimen name="keyguard_affordance_touch_target_size">120dp</dimen>
<!-- The grow amount for the camera and phone circles when hinting -->
<dimen name="hint_grow_amount_sideways">60dp</dimen>
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
index 0369ab5..5d74604 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/FlashlightTile.java
@@ -74,7 +74,11 @@
state.visible = mFlashlightController.isAvailable();
state.label = mHost.getContext().getString(R.string.quick_settings_flashlight_label);
if (arg instanceof UserBoolean) {
- state.value = ((UserBoolean) arg).value;
+ boolean value = ((UserBoolean) arg).value;
+ if (value == state.value) {
+ return;
+ }
+ state.value = value;
}
final AnimationIcon icon = state.value ? mEnable : mDisable;
icon.setAllowAnimation(arg instanceof UserBoolean && ((UserBoolean) arg).userInitiated);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
index c702673..bbd3e60 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Recents.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
@@ -35,6 +35,7 @@
import android.os.Handler;
import android.os.SystemClock;
import android.os.UserHandle;
+import android.util.MutableBoolean;
import android.util.Pair;
import android.view.Display;
import android.view.LayoutInflater;
@@ -57,7 +58,6 @@
import com.android.systemui.statusbar.phone.PhoneStatusBar;
import java.util.ArrayList;
-import java.util.concurrent.atomic.AtomicBoolean;
/**
* Annotation for a method that is only called from the primary user's SystemUI process and will be
@@ -362,7 +362,12 @@
// RecentsActivity)
RecentsTaskLoader loader = RecentsTaskLoader.getInstance();
sInstanceLoadPlan = loader.createLoadPlan(mContext);
- sInstanceLoadPlan.preloadRawTasks(true);
+
+ ActivityManager.RunningTaskInfo topTask = mSystemServicesProxy.getTopMostTask();
+ MutableBoolean isTopTaskHome = new MutableBoolean(true);
+ if (topTask != null && mSystemServicesProxy.isRecentsTopMost(topTask, isTopTaskHome)) {
+ sInstanceLoadPlan.preloadRawTasks(isTopTaskHome.value);
+ }
}
@Override
@@ -546,7 +551,7 @@
// If Recents is the front most activity, then we should just communicate with it directly
// to launch the first task or dismiss itself
ActivityManager.RunningTaskInfo topTask = mSystemServicesProxy.getTopMostTask();
- AtomicBoolean isTopTaskHome = new AtomicBoolean(true);
+ MutableBoolean isTopTaskHome = new MutableBoolean(true);
if (topTask != null && mSystemServicesProxy.isRecentsTopMost(topTask, isTopTaskHome)) {
// Notify recents to toggle itself
Intent intent = createLocalBroadcastIntent(mContext, ACTION_TOGGLE_RECENTS_ACTIVITY);
@@ -555,7 +560,7 @@
return;
} else {
// Otherwise, start the recents activity
- startRecentsActivity(topTask, isTopTaskHome.get());
+ startRecentsActivity(topTask, isTopTaskHome.value);
}
}
@@ -563,9 +568,9 @@
void startRecentsActivity() {
// Check if the top task is in the home stack, and start the recents activity
ActivityManager.RunningTaskInfo topTask = mSystemServicesProxy.getTopMostTask();
- AtomicBoolean isTopTaskHome = new AtomicBoolean(true);
+ MutableBoolean isTopTaskHome = new MutableBoolean(true);
if (topTask == null || !mSystemServicesProxy.isRecentsTopMost(topTask, isTopTaskHome)) {
- startRecentsActivity(topTask, isTopTaskHome.get());
+ startRecentsActivity(topTask, isTopTaskHome.value);
}
}
@@ -654,6 +659,7 @@
if (task == null) {
// If no task is specified or we can not find the task just use the front most one
task = tasks.get(tasks.size() - 1);
+ runningTaskOut.copyFrom(task);
}
// Get the transform for the running task
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
index ca0f357..272d39a 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -54,6 +54,7 @@
import android.os.UserHandle;
import android.provider.Settings;
import android.util.Log;
+import android.util.MutableBoolean;
import android.util.Pair;
import android.util.SparseArray;
import android.view.Display;
@@ -67,12 +68,9 @@
import java.io.IOException;
import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Random;
-import java.util.concurrent.atomic.AtomicBoolean;
/**
* Acts as a shim around the real system services that we need to access data from, and provides
@@ -192,7 +190,7 @@
// Break early if we can't get a valid set of tasks
if (tasks == null) {
- return new ArrayList<ActivityManager.RecentTaskInfo>();
+ return new ArrayList<>();
}
boolean isFirstValidTask = true;
@@ -235,7 +233,7 @@
/** Returns whether the recents is currently running */
public boolean isRecentsTopMost(ActivityManager.RunningTaskInfo topTask,
- AtomicBoolean isHomeTopMost) {
+ MutableBoolean isHomeTopMost) {
if (topTask != null) {
ComponentName topActivity = topTask.topActivity;
@@ -243,13 +241,13 @@
if (topActivity.getPackageName().equals(Recents.sRecentsPackage) &&
topActivity.getClassName().equals(Recents.sRecentsActivity)) {
if (isHomeTopMost != null) {
- isHomeTopMost.set(false);
+ isHomeTopMost.value = false;
}
return true;
}
if (isHomeTopMost != null) {
- isHomeTopMost.set(isInHomeStack(topTask.id));
+ isHomeTopMost.value = isInHomeStack(topTask.id);
}
}
return false;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
index 40cd211..f40c58d 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/model/RecentsTaskLoadPlan.java
@@ -104,13 +104,9 @@
if (mRawTasks == null) {
preloadRawTasks(isTopTaskHome);
}
- int firstStackId = -1;
int taskCount = mRawTasks.size();
for (int i = 0; i < taskCount; i++) {
ActivityManager.RecentTaskInfo t = mRawTasks.get(i);
- if (firstStackId < 0) {
- firstStackId = t.stackId;
- }
// Compose the task key
Task.TaskKey taskKey = new Task.TaskKey(t.persistentId, t.stackId, t.baseIntent,
@@ -158,17 +154,17 @@
if (!mConfig.multiStackEnabled ||
Constants.DebugFlags.App.EnableMultiStackToSingleStack) {
- firstStackId = 0;
+ int firstStackId = 0;
ArrayList<Task> stackTasks = stacksTasks.get(firstStackId);
if (stackTasks == null) {
- stackTasks = new ArrayList<Task>();
+ stackTasks = new ArrayList<>();
stacksTasks.put(firstStackId, stackTasks);
}
stackTasks.add(task);
} else {
ArrayList<Task> stackTasks = stacksTasks.get(t.stackId);
if (stackTasks == null) {
- stackTasks = new ArrayList<Task>();
+ stackTasks = new ArrayList<>();
stacksTasks.put(t.stackId, stackTasks);
}
stackTasks.add(task);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
index b3e6221..cec613c 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/RecentsView.java
@@ -550,7 +550,7 @@
if (tv == null) {
launchRunnable.run();
} else {
- if (!task.group.isFrontMostTask(task)) {
+ if (task.group != null && !task.group.isFrontMostTask(task)) {
// For affiliated tasks that are behind other tasks, we must animate the front cards
// out of view before starting the task transition
stackView.startLaunchTaskAnimation(tv, launchRunnable, lockToTask);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java
index 9ccff72..374d970 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardAffordanceView.java
@@ -23,11 +23,14 @@
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Canvas;
+import android.graphics.CanvasProperty;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
+import android.view.DisplayListCanvas;
+import android.view.RenderNodeAnimator;
import android.view.View;
import android.view.ViewAnimationUtils;
import android.view.animation.AnimationUtils;
@@ -35,6 +38,7 @@
import android.widget.ImageView;
import com.android.systemui.R;
import com.android.systemui.statusbar.phone.KeyguardAffordanceHelper;
+import com.android.systemui.statusbar.phone.PhoneStatusBar;
/**
* An ImageView which does not have overlapping renderings commands and therefore does not need a
@@ -77,6 +81,14 @@
private float mMaxCircleSize;
private Animator mPreviewClipper;
private float mRestingAlpha = KeyguardAffordanceHelper.SWIPE_RESTING_ALPHA_AMOUNT;
+ private boolean mSupportHardware;
+ private boolean mFinishing;
+
+ private CanvasProperty<Float> mHwCircleRadius;
+ private CanvasProperty<Float> mHwCenterX;
+ private CanvasProperty<Float> mHwCenterY;
+ private CanvasProperty<Paint> mHwCirclePaint;
+
private AnimatorListenerAdapter mClipEndListener = new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
@@ -155,6 +167,7 @@
@Override
protected void onDraw(Canvas canvas) {
+ mSupportHardware = canvas.isHardwareAccelerated();
drawBackgroundCircle(canvas);
drawArrow(canvas);
canvas.save();
@@ -196,8 +209,14 @@
private void drawBackgroundCircle(Canvas canvas) {
if (mCircleRadius > 0) {
- updateCircleColor();
- canvas.drawCircle(mCenterX, mCenterY, mCircleRadius, mCirclePaint);
+ if (mFinishing && mSupportHardware) {
+ DisplayListCanvas displayListCanvas = (DisplayListCanvas) canvas;
+ displayListCanvas.drawCircle(mHwCenterX, mHwCenterY, mHwCircleRadius,
+ mHwCirclePaint);
+ } else {
+ updateCircleColor();
+ canvas.drawCircle(mCenterX, mCenterY, mCircleRadius, mCirclePaint);
+ }
}
}
@@ -218,15 +237,23 @@
public void finishAnimation(float velocity, final Runnable mAnimationEndRunnable) {
cancelAnimator(mCircleAnimator);
cancelAnimator(mPreviewClipper);
+ mFinishing = true;
mCircleStartRadius = mCircleRadius;
float maxCircleSize = getMaxCircleSize();
- ValueAnimator animatorToRadius = getAnimatorToRadius(maxCircleSize);
+ Animator animatorToRadius;
+ if (mSupportHardware) {
+ initHwProperties();
+ animatorToRadius = getRtAnimatorToRadius(maxCircleSize);
+ } else {
+ animatorToRadius = getAnimatorToRadius(maxCircleSize);
+ }
mFlingAnimationUtils.applyDismissing(animatorToRadius, mCircleRadius, maxCircleSize,
velocity, maxCircleSize);
animatorToRadius.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
mAnimationEndRunnable.run();
+ mFinishing = false;
}
});
animatorToRadius.start();
@@ -240,9 +267,34 @@
velocity, maxCircleSize);
mPreviewClipper.addListener(mClipEndListener);
mPreviewClipper.start();
+ if (mSupportHardware) {
+ startRtCircleFadeOut(animatorToRadius.getDuration());
+ }
}
}
+ private void startRtCircleFadeOut(long duration) {
+ RenderNodeAnimator animator = new RenderNodeAnimator(mHwCirclePaint,
+ RenderNodeAnimator.PAINT_ALPHA, 0);
+ animator.setDuration(duration);
+ animator.setInterpolator(PhoneStatusBar.ALPHA_OUT);
+ animator.setTarget(this);
+ animator.start();
+ }
+
+ private Animator getRtAnimatorToRadius(float circleRadius) {
+ RenderNodeAnimator animator = new RenderNodeAnimator(mHwCircleRadius, circleRadius);
+ animator.setTarget(this);
+ return animator;
+ }
+
+ private void initHwProperties() {
+ mHwCenterX = CanvasProperty.createFloat(mCenterX);
+ mHwCenterY = CanvasProperty.createFloat(mCenterY);
+ mHwCirclePaint = CanvasProperty.createPaint(mCirclePaint);
+ mHwCircleRadius = CanvasProperty.createFloat(mCircleRadius);
+ }
+
private float getMaxCircleSize() {
getLocationInWindow(mTempPoint);
float rootWidth = getRootView().getWidth();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index 2fe98bb..4bc317a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -196,6 +196,7 @@
private boolean mCollapsedOnDown;
private int mPositionMinSideMargin;
private int mLastOrientation = -1;
+ private boolean mClosingWithAlphaFadeOut;
private Runnable mHeadsUpExistenceChangedRunnable = new Runnable() {
@Override
@@ -527,6 +528,7 @@
protected void flingToHeight(float vel, boolean expand, float target,
float collapseSpeedUpFactor, boolean expandBecauseOfFalsing) {
mHeadsUpTouchHelper.notifyFling(!expand);
+ setClosingWithAlphaFadeout(!expand && getFadeoutAlpha() == 1.0f);
super.flingToHeight(vel, expand, target, collapseSpeedUpFactor, expandBecauseOfFalsing);
}
@@ -638,10 +640,9 @@
@Override
protected boolean isInContentBounds(float x, float y) {
- float yTransformed = y - mNotificationStackScroller.getY();
float stackScrollerX = mNotificationStackScroller.getX();
- return mNotificationStackScroller.isInContentBounds(yTransformed) && stackScrollerX < x
- && x < stackScrollerX + mNotificationStackScroller.getWidth();
+ return !mNotificationStackScroller.isBelowLastNotification(x - stackScrollerX, y)
+ && stackScrollerX < x && x < stackScrollerX + mNotificationStackScroller.getWidth();
}
private void initDownStates(MotionEvent event) {
@@ -1074,8 +1075,12 @@
};
private void animateHeaderSlidingIn() {
- mHeaderAnimating = true;
- getViewTreeObserver().addOnPreDrawListener(mStartHeaderSlidingIn);
+ // If the QS is already expanded we don't need to slide in the header as it's already
+ // visible.
+ if (!mQsExpanded) {
+ mHeaderAnimating = true;
+ getViewTreeObserver().addOnPreDrawListener(mStartHeaderSlidingIn);
+ }
}
private void animateHeaderSlidingOut() {
@@ -1585,26 +1590,22 @@
}
}
private void updateNotificationTranslucency() {
- float alpha;
- if (mExpandingFromHeadsUp || mHeadsUpManager.hasPinnedHeadsUp()) {
- alpha = 1f;
- } else {
- alpha = (getNotificationsTopY() + mNotificationStackScroller.getItemHeight())
- / (mQsMinExpansionHeight + mNotificationStackScroller.getBottomStackPeekSize()
- - mNotificationStackScroller.getCollapseSecondCardPadding());
- alpha = Math.max(0, Math.min(alpha, 1));
- alpha = (float) Math.pow(alpha, 0.75);
- }
-
- if (alpha != 1f && mNotificationStackScroller.getLayerType() != LAYER_TYPE_HARDWARE) {
- mNotificationStackScroller.setLayerType(LAYER_TYPE_HARDWARE, null);
- } else if (alpha == 1f
- && mNotificationStackScroller.getLayerType() == LAYER_TYPE_HARDWARE) {
- mNotificationStackScroller.setLayerType(LAYER_TYPE_NONE, null);
+ float alpha = 1f;
+ if (mClosingWithAlphaFadeOut && !mExpandingFromHeadsUp && !mHeadsUpManager.hasPinnedHeadsUp()) {
+ alpha = getFadeoutAlpha();
}
mNotificationStackScroller.setAlpha(alpha);
}
+ private float getFadeoutAlpha() {
+ float alpha = (getNotificationsTopY() + mNotificationStackScroller.getItemHeight())
+ / (mQsMinExpansionHeight + mNotificationStackScroller.getBottomStackPeekSize()
+ - mNotificationStackScroller.getCollapseSecondCardPadding());
+ alpha = Math.max(0, Math.min(alpha, 1));
+ alpha = (float) Math.pow(alpha, 0.75);
+ return alpha;
+ }
+
@Override
protected float getOverExpansionAmount() {
return mNotificationStackScroller.getCurrentOverScrollAmount(true /* top */);
@@ -2261,6 +2262,12 @@
protected void onClosingFinished() {
super.onClosingFinished();
resetVerticalPanelPosition();
+ setClosingWithAlphaFadeout(false);
+ }
+
+ private void setClosingWithAlphaFadeout(boolean closing) {
+ mClosingWithAlphaFadeOut = closing;
+ mNotificationStackScroller.forceNoOverlappingRendering(closing);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
index 6d35ff0..9d4997c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
@@ -777,6 +777,15 @@
public void setExpandedFraction(float frac) {
setExpandedHeight(getMaxPanelHeight() * frac);
+ if (PhoneStatusBar.DEBUG_EMPTY_KEYGUARD
+ && mStatusBar.getBarState() == StatusBarState.KEYGUARD) {
+ if (frac == 0.0f) {
+ Log.i(PhoneStatusBar.TAG, "Panel collapsed! Stacktrace: "
+ + Log.getStackTraceString(new Throwable()));
+ } else if (frac == 1.0f) {
+ mStatusBar.endWindowManagerLogging();
+ }
+ }
}
public float getExpandedHeight() {
@@ -808,6 +817,11 @@
}
public void collapse(boolean delayed, float speedUpFactor) {
+ if (PhoneStatusBar.DEBUG_EMPTY_KEYGUARD
+ && mStatusBar.getBarState() == StatusBarState.KEYGUARD) {
+ Log.i(PhoneStatusBar.TAG, "Panel collapsed! Stacktrace: "
+ + Log.getStackTraceString(new Throwable()));
+ }
if (DEBUG) logf("collapse: " + this);
if (mPeekPending || mPeekAnimator != null) {
mCollapseAfterPeek = true;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index def95aa..7c7bec9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -185,6 +185,7 @@
HeadsUpManager.OnHeadsUpChangedListener {
static final String TAG = "PhoneStatusBar";
public static final boolean DEBUG = BaseStatusBar.DEBUG;
+ public static final boolean DEBUG_EMPTY_KEYGUARD = true;
public static final boolean SPEW = false;
public static final boolean DUMPTRUCK = true; // extra dumpsys info
public static final boolean DEBUG_GESTURES = false;
@@ -2003,6 +2004,10 @@
mStatusBarWindowManager.setPanelExpanded(isExpanded);
}
+ public void endWindowManagerLogging() {
+ mStatusBarWindowManager.setLogState(false);
+ }
+
/**
* All changes to the status bar and notifications funnel through here and are batched.
*/
@@ -3547,6 +3552,9 @@
// Make our window larger and the panel expanded.
makeExpandedVisible(true);
mNotificationPanel.instantExpand();
+ if (DEBUG_EMPTY_KEYGUARD) {
+ mStatusBarWindowManager.setLogState(true);
+ }
}
private void instantCollapseNotificationPanel() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
index aa499ad..b7e675d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
@@ -20,12 +20,14 @@
import android.content.res.Resources;
import android.util.AttributeSet;
import android.util.EventLog;
+import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.accessibility.AccessibilityEvent;
import com.android.systemui.EventLogTags;
import com.android.systemui.R;
+import com.android.systemui.statusbar.StatusBarState;
public class PhoneStatusBarView extends PanelBar {
private static final String TAG = "PhoneStatusBarView";
@@ -108,7 +110,11 @@
@Override
public void onAllPanelsCollapsed() {
super.onAllPanelsCollapsed();
-
+ if (PhoneStatusBar.DEBUG_EMPTY_KEYGUARD
+ && mBar.getBarState() == StatusBarState.KEYGUARD) {
+ Log.i(PhoneStatusBar.TAG, "Panel collapsed! Stacktrace: "
+ + Log.getStackTraceString(new Throwable()));
+ }
// Close the status bar in the next frame so we can show the end of the animation.
postOnAnimation(new Runnable() {
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
index de426430..58017d0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
@@ -21,6 +21,7 @@
import android.content.res.Resources;
import android.graphics.PixelFormat;
import android.os.SystemProperties;
+import android.util.Log;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
@@ -31,6 +32,8 @@
import com.android.systemui.statusbar.BaseStatusBar;
import com.android.systemui.statusbar.StatusBarState;
+import java.lang.reflect.Field;
+
/**
* Encapsulates all logic for the status bar window state management.
*/
@@ -45,6 +48,7 @@
private final boolean mKeyguardScreenRotation;
private final State mCurrentState = new State();
+ private boolean mLogState;
public StatusBarWindowManager(Context context) {
mContext = context;
@@ -129,9 +133,7 @@
}
private void applyHeight(State state) {
- boolean expanded = !state.forceCollapsed && (state.isKeyguardShowingAndNotOccluded()
- || state.panelVisible || state.keyguardFadingAway || state.bouncerShowing
- || state.headsUpShowing);
+ boolean expanded = isExpanded(state);
if (expanded) {
mLpChanged.height = ViewGroup.LayoutParams.MATCH_PARENT;
} else {
@@ -139,6 +141,12 @@
}
}
+ private boolean isExpanded(State state) {
+ return !state.forceCollapsed && (state.isKeyguardShowingAndNotOccluded()
+ || state.panelVisible || state.keyguardFadingAway || state.bouncerShowing
+ || state.headsUpShowing);
+ }
+
private void applyFitsSystemWindows(State state) {
mStatusBarView.setFitsSystemWindows(!state.isKeyguardShowingAndNotOccluded());
}
@@ -176,6 +184,9 @@
applyFitsSystemWindows(state);
applyModalFlag(state);
if (mLp.copyFrom(mLpChanged) != 0) {
+ if (PhoneStatusBar.DEBUG_EMPTY_KEYGUARD && mLogState) {
+ logCurrentState();
+ }
mWindowManager.updateViewLayout(mStatusBarView, mLp);
}
}
@@ -272,6 +283,21 @@
apply(mCurrentState);
}
+ public void setLogState(boolean logState) {
+ mLogState = logState;
+ if (logState) {
+ Log.w(PhoneStatusBar.TAG, "===== Started logging WM state changes =====");
+ logCurrentState();
+ } else {
+ Log.w(PhoneStatusBar.TAG, "===== Finished logging WM state changes =====");
+ }
+ }
+
+ private void logCurrentState() {
+ Log.i(PhoneStatusBar.TAG, mCurrentState.toString()
+ + "\n Expanded: " + isExpanded(mCurrentState));
+ }
+
private static class State {
boolean keyguardShowing;
boolean keyguardOccluded;
@@ -294,5 +320,31 @@
private boolean isKeyguardShowingAndNotOccluded() {
return keyguardShowing && !keyguardOccluded;
}
+
+ @Override
+ public String toString() {
+ StringBuilder result = new StringBuilder();
+ String newLine = "\n";
+ result.append("Window State {");
+ result.append(newLine);
+
+ Field[] fields = this.getClass().getDeclaredFields();
+
+ // Print field names paired with their values
+ for (Field field : fields) {
+ result.append(" ");
+ try {
+ result.append(field.getName());
+ result.append(": ");
+ //requires access to private field:
+ result.append(field.get(this));
+ } catch (IllegalAccessException ex) {
+ }
+ result.append(newLine);
+ }
+ result.append("}");
+
+ return result.toString();
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
index 3b91751..6a8f8ee 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowView.java
@@ -171,7 +171,6 @@
if (mNotificationPanel.isFullyExpanded()
&& mStackScrollLayout.getVisibility() == View.VISIBLE
&& mService.getBarState() == StatusBarState.KEYGUARD
- && !mService.isQsExpanded()
&& !mService.isBouncerShowing()) {
intercept = mDragDownHelper.onInterceptTouchEvent(ev);
// wake up on a touch down event, if dozing
@@ -195,7 +194,7 @@
@Override
public boolean onTouchEvent(MotionEvent ev) {
boolean handled = false;
- if (mService.getBarState() == StatusBarState.KEYGUARD && !mService.isQsExpanded()) {
+ if (mService.getBarState() == StatusBarState.KEYGUARD) {
handled = mDragDownHelper.onTouchEvent(ev);
}
if (!handled) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
index 4ae800f..d8f6bcd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/stack/NotificationStackScrollLayout.java
@@ -225,6 +225,7 @@
private HeadsUpManager mHeadsUpManager;
private boolean mTrackingHeadsUp;
private ScrimController mScrimController;
+ private boolean mForceNoOverlappingRendering;
public NotificationStackScrollLayout(Context context) {
this(context, null);
@@ -811,8 +812,7 @@
}
handleEmptySpaceClick(ev);
boolean expandWantsIt = false;
- if (mIsExpanded && !mSwipingInProgress && !mOnlyScrollingInThisMotion
- && isScrollingEnabled()) {
+ if (mIsExpanded && !mSwipingInProgress && !mOnlyScrollingInThisMotion) {
if (isCancelOrUp) {
mExpandHelper.onlyObserveMovements(false);
}
@@ -1566,7 +1566,7 @@
initDownStates(ev);
handleEmptySpaceClick(ev);
boolean expandWantsIt = false;
- if (!mSwipingInProgress && !mOnlyScrollingInThisMotion && isScrollingEnabled()) {
+ if (!mSwipingInProgress && !mOnlyScrollingInThisMotion) {
expandWantsIt = mExpandHelper.onInterceptTouchEvent(ev);
}
boolean scrollWantsIt = false;
@@ -2267,11 +2267,11 @@
private void updateScrollPositionOnExpandInBottom(ExpandableView view) {
if (view instanceof ExpandableNotificationRow) {
ExpandableNotificationRow row = (ExpandableNotificationRow) view;
- if (row.isUserLocked()) {
+ if (row.isUserLocked() && row != getFirstChildNotGone()) {
// We are actually expanding this view
float endPosition = row.getTranslationY() + row.getActualHeight();
int stackEnd = mMaxLayoutHeight - mBottomStackPeekSize -
- mBottomStackSlowDownHeight;
+ mBottomStackSlowDownHeight + (int) mStackTranslation;
if (endPosition > stackEnd) {
mOwnScrollY += endPosition - stackEnd;
mDisallowScrollingInThisMotion = true;
@@ -2614,7 +2614,7 @@
}
}
- private boolean isBelowLastNotification(float touchX, float touchY) {
+ public boolean isBelowLastNotification(float touchX, float touchY) {
int childCount = getChildCount();
for (int i = childCount - 1; i >= 0; i--) {
ExpandableView child = (ExpandableView) getChildAt(i);
@@ -2640,7 +2640,7 @@
}
}
}
- return touchY > mIntrinsicPadding;
+ return touchY > mTopPadding + mStackTranslation;
}
private void updateExpandButtons() {
@@ -2732,6 +2732,15 @@
mScrimController = scrimController;
}
+ public void forceNoOverlappingRendering(boolean force) {
+ mForceNoOverlappingRendering = force;
+ }
+
+ @Override
+ public boolean hasOverlappingRendering() {
+ return !mForceNoOverlappingRendering && super.hasOverlappingRendering();
+ }
+
/**
* A listener that is notified when some child locations might have changed.
*/
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index d4c3194..7bcbcfb 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -166,8 +166,6 @@
private final List<AccessibilityServiceInfo> mEnabledServicesForFeedbackTempList =
new ArrayList<>();
- private final Region mTempRegion = new Region();
-
private final Rect mTempRect = new Rect();
private final Rect mTempRect1 = new Rect();
@@ -2241,7 +2239,7 @@
throws RemoteException {
final int resolvedWindowId;
IAccessibilityInteractionConnection connection = null;
- Region partialInteractiveRegion = mTempRegion;
+ Region partialInteractiveRegion = Region.obtain();
synchronized (mLock) {
// We treat calls from a profile as if made by its parent as profiles
// share the accessibility state of the parent. The call below
@@ -2265,6 +2263,7 @@
}
if (!mSecurityPolicy.computePartialInteractiveRegionForWindowLocked(
resolvedWindowId, partialInteractiveRegion)) {
+ partialInteractiveRegion.recycle();
partialInteractiveRegion = null;
}
}
@@ -2282,6 +2281,10 @@
}
} finally {
Binder.restoreCallingIdentity(identityToken);
+ // Recycle if passed to another process.
+ if (partialInteractiveRegion != null && Binder.isProxy(connection)) {
+ partialInteractiveRegion.recycle();
+ }
}
return false;
}
@@ -2293,7 +2296,7 @@
throws RemoteException {
final int resolvedWindowId;
IAccessibilityInteractionConnection connection = null;
- Region partialInteractiveRegion = mTempRegion;
+ Region partialInteractiveRegion = Region.obtain();
synchronized (mLock) {
// We treat calls from a profile as if made by its parent as profiles
// share the accessibility state of the parent. The call below
@@ -2317,6 +2320,7 @@
}
if (!mSecurityPolicy.computePartialInteractiveRegionForWindowLocked(
resolvedWindowId, partialInteractiveRegion)) {
+ partialInteractiveRegion.recycle();
partialInteractiveRegion = null;
}
}
@@ -2334,6 +2338,10 @@
}
} finally {
Binder.restoreCallingIdentity(identityToken);
+ // Recycle if passed to another process.
+ if (partialInteractiveRegion != null && Binder.isProxy(connection)) {
+ partialInteractiveRegion.recycle();
+ }
}
return false;
}
@@ -2345,7 +2353,7 @@
long interrogatingTid) throws RemoteException {
final int resolvedWindowId;
IAccessibilityInteractionConnection connection = null;
- Region partialInteractiveRegion = mTempRegion;
+ Region partialInteractiveRegion = Region.obtain();
synchronized (mLock) {
// We treat calls from a profile as if made by its parent as profiles
// share the accessibility state of the parent. The call below
@@ -2369,6 +2377,7 @@
}
if (!mSecurityPolicy.computePartialInteractiveRegionForWindowLocked(
resolvedWindowId, partialInteractiveRegion)) {
+ partialInteractiveRegion.recycle();
partialInteractiveRegion = null;
}
}
@@ -2386,6 +2395,10 @@
}
} finally {
Binder.restoreCallingIdentity(identityToken);
+ // Recycle if passed to another process.
+ if (partialInteractiveRegion != null && Binder.isProxy(connection)) {
+ partialInteractiveRegion.recycle();
+ }
}
return false;
}
@@ -2397,7 +2410,7 @@
throws RemoteException {
final int resolvedWindowId;
IAccessibilityInteractionConnection connection = null;
- Region partialInteractiveRegion = mTempRegion;
+ Region partialInteractiveRegion = Region.obtain();
synchronized (mLock) {
// We treat calls from a profile as if made by its parent as profiles
// share the accessibility state of the parent. The call below
@@ -2422,6 +2435,7 @@
}
if (!mSecurityPolicy.computePartialInteractiveRegionForWindowLocked(
resolvedWindowId, partialInteractiveRegion)) {
+ partialInteractiveRegion.recycle();
partialInteractiveRegion = null;
}
}
@@ -2439,6 +2453,10 @@
}
} finally {
Binder.restoreCallingIdentity(identityToken);
+ // Recycle if passed to another process.
+ if (partialInteractiveRegion != null && Binder.isProxy(connection)) {
+ partialInteractiveRegion.recycle();
+ }
}
return false;
}
@@ -2450,7 +2468,7 @@
throws RemoteException {
final int resolvedWindowId;
IAccessibilityInteractionConnection connection = null;
- Region partialInteractiveRegion = mTempRegion;
+ Region partialInteractiveRegion = Region.obtain();
synchronized (mLock) {
// We treat calls from a profile as if made by its parent as profiles
// share the accessibility state of the parent. The call below
@@ -2474,6 +2492,7 @@
}
if (!mSecurityPolicy.computePartialInteractiveRegionForWindowLocked(
resolvedWindowId, partialInteractiveRegion)) {
+ partialInteractiveRegion.recycle();
partialInteractiveRegion = null;
}
}
@@ -2491,6 +2510,10 @@
}
} finally {
Binder.restoreCallingIdentity(identityToken);
+ // Recycle if passed to another process.
+ if (partialInteractiveRegion != null && Binder.isProxy(connection)) {
+ partialInteractiveRegion.recycle();
+ }
}
return false;
}
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 963d23d..44d00d7 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -1661,6 +1661,12 @@
}
private static final String DEFAULT_TCP_BUFFER_SIZES = "4096,87380,110208,4096,16384,110208";
+ private static final String DEFAULT_TCP_RWND_KEY = "net.tcp.default_init_rwnd";
+
+ // Overridden for testing purposes to avoid writing to SystemProperties.
+ protected int getDefaultTcpRwnd() {
+ return SystemProperties.getInt(DEFAULT_TCP_RWND_KEY, 0);
+ }
private void updateTcpBufferSizes(NetworkAgentInfo nai) {
if (isDefaultNetwork(nai) == false) {
@@ -1696,10 +1702,8 @@
loge("Can't set TCP buffer sizes:" + e);
}
- final String defaultRwndKey = "net.tcp.default_init_rwnd";
- int defaultRwndValue = SystemProperties.getInt(defaultRwndKey, 0);
Integer rwndValue = Settings.Global.getInt(mContext.getContentResolver(),
- Settings.Global.TCP_DEFAULT_INIT_RWND, defaultRwndValue);
+ Settings.Global.TCP_DEFAULT_INIT_RWND, getDefaultTcpRwnd());
final String sysctlKey = "sys.sysctl.tcp_def_init_rwnd";
if (rwndValue != 0) {
SystemProperties.set(sysctlKey, rwndValue.toString());
@@ -3819,10 +3823,12 @@
// }
updateTcpBufferSizes(networkAgent);
- // TODO: deprecate and remove mDefaultDns when we can do so safely.
- // For now, use it only when the network has Internet access. http://b/18327075
- final boolean useDefaultDns = networkAgent.networkCapabilities.hasCapability(
- NET_CAPABILITY_INTERNET);
+ // TODO: deprecate and remove mDefaultDns when we can do so safely. See http://b/18327075
+ // In L, we used it only when the network had Internet access but provided no DNS servers.
+ // For now, just disable it, and if disabling it doesn't break things, remove it.
+ // final boolean useDefaultDns = networkAgent.networkCapabilities.hasCapability(
+ // NET_CAPABILITY_INTERNET);
+ final boolean useDefaultDns = false;
final boolean flushDns = updateRoutes(newLp, oldLp, netId);
updateDnses(newLp, oldLp, netId, flushDns, useDefaultDns);
diff --git a/services/core/java/com/android/server/InputMethodManagerService.java b/services/core/java/com/android/server/InputMethodManagerService.java
index 201a4ca..0513d8b 100644
--- a/services/core/java/com/android/server/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/InputMethodManagerService.java
@@ -960,26 +960,33 @@
}
private void switchUserLocked(int newUserId) {
+ if (DEBUG) Slog.d(TAG, "Switching user stage 1/3. newUserId=" + newUserId
+ + " currentUserId=" + mSettings.getCurrentUserId());
+
mSettings.setCurrentUserId(newUserId);
updateCurrentProfileIds();
// InputMethodFileManager should be reset when the user is changed
mFileManager = new InputMethodFileManager(mMethodMap, newUserId);
final String defaultImiId = mSettings.getSelectedInputMethod();
+
+ if (DEBUG) Slog.d(TAG, "Switching user stage 2/3. newUserId=" + newUserId
+ + " defaultImiId=" + defaultImiId);
+
// For secondary users, the list of enabled IMEs may not have been updated since the
// callbacks to PackageMonitor are ignored for the secondary user. Here, defaultImiId may
// not be empty even if the IME has been uninstalled by the primary user.
// Even in such cases, IMMS works fine because it will find the most applicable
// IME for that user.
final boolean initialUserSwitch = TextUtils.isEmpty(defaultImiId);
- if (DEBUG) {
- Slog.d(TAG, "Switch user: " + newUserId + " current ime = " + defaultImiId);
- }
resetAllInternalStateLocked(false /* updateOnlyWhenLocaleChanged */,
initialUserSwitch /* needsToResetDefaultIme */);
if (initialUserSwitch) {
InputMethodUtils.setNonSelectedSystemImesDisabledUntilUsed(mContext.getPackageManager(),
mSettings.getEnabledInputMethodListLocked());
}
+
+ if (DEBUG) Slog.d(TAG, "Switching user stage 3/3. newUserId=" + newUserId
+ + " selectedIme=" + mSettings.getSelectedInputMethod());
}
void updateCurrentProfileIds() {
@@ -3706,6 +3713,8 @@
p.println(" mCurUserActionNotificationSequenceNumber="
+ mCurUserActionNotificationSequenceNumber);
p.println(" mSystemReady=" + mSystemReady + " mInteractive=" + mScreenOn);
+ p.println(" mSwitchingController:");
+ mSwitchingController.dump(p);
}
p.println(" ");
diff --git a/services/core/java/com/android/server/accounts/AccountManagerService.java b/services/core/java/com/android/server/accounts/AccountManagerService.java
index 3315c89..21f96c9 100644
--- a/services/core/java/com/android/server/accounts/AccountManagerService.java
+++ b/services/core/java/com/android/server/accounts/AccountManagerService.java
@@ -1785,7 +1785,13 @@
// Get the calling package. We will use it for the purpose of caching.
final String callerPkg = loginOptions.getString(AccountManager.KEY_ANDROID_PACKAGE_NAME);
- List<String> callerOwnedPackageNames = Arrays.asList(mPackageManager.getPackagesForUid(callerUid));
+ List<String> callerOwnedPackageNames;
+ long ident = Binder.clearCallingIdentity();
+ try {
+ callerOwnedPackageNames = Arrays.asList(mPackageManager.getPackagesForUid(callerUid));
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
if (callerPkg == null || !callerOwnedPackageNames.contains(callerPkg)) {
String msg = String.format(
"Uid %s is attempting to illegally masquerade as package %s!",
@@ -1798,15 +1804,15 @@
loginOptions.putInt(AccountManager.KEY_CALLER_UID, callerUid);
loginOptions.putInt(AccountManager.KEY_CALLER_PID, Binder.getCallingPid());
- // Distill the caller's package signatures into a single digest.
- final byte[] callerPkgSigDigest = calculatePackageSignatureDigest(callerPkg);
-
if (notifyOnAuthFailure) {
loginOptions.putBoolean(AccountManager.KEY_NOTIFY_ON_FAILURE, true);
}
long identityToken = clearCallingIdentity();
try {
+ // Distill the caller's package signatures into a single digest.
+ final byte[] callerPkgSigDigest = calculatePackageSignatureDigest(callerPkg);
+
// if the caller has permission, do the peek. otherwise go the more expensive
// route of starting a Session
if (!customTokens && permissionGranted) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 81b8457..64e30e5 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -39,13 +39,13 @@
import android.Manifest;
import android.app.AppOpsManager;
import android.app.ApplicationThreadNative;
-import android.app.AssistContent;
-import android.app.AssistStructure;
import android.app.IActivityContainer;
import android.app.IActivityContainerCallback;
import android.app.IAppTask;
import android.app.ITaskStackListener;
import android.app.ProfilerInfo;
+import android.app.assist.AssistContent;
+import android.app.assist.AssistStructure;
import android.app.usage.UsageEvents;
import android.app.usage.UsageStatsManagerInternal;
import android.appwidget.AppWidgetManager;
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index 745cb7e..80b8a93 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -1121,25 +1121,23 @@
}
final void logBroadcastReceiverDiscardLocked(BroadcastRecord r) {
- if (r.nextReceiver > 0) {
- Object curReceiver = r.receivers.get(r.nextReceiver-1);
+ final int logIndex = r.nextReceiver - 1;
+ if (logIndex >= 0 && logIndex < r.receivers.size()) {
+ Object curReceiver = r.receivers.get(logIndex);
if (curReceiver instanceof BroadcastFilter) {
BroadcastFilter bf = (BroadcastFilter) curReceiver;
EventLog.writeEvent(EventLogTags.AM_BROADCAST_DISCARD_FILTER,
bf.owningUserId, System.identityHashCode(r),
- r.intent.getAction(),
- r.nextReceiver - 1,
- System.identityHashCode(bf));
+ r.intent.getAction(), logIndex, System.identityHashCode(bf));
} else {
- ResolveInfo ri = (ResolveInfo)curReceiver;
+ ResolveInfo ri = (ResolveInfo) curReceiver;
EventLog.writeEvent(EventLogTags.AM_BROADCAST_DISCARD_APP,
UserHandle.getUserId(ri.activityInfo.applicationInfo.uid),
- System.identityHashCode(r), r.intent.getAction(),
- r.nextReceiver - 1, ri.toString());
+ System.identityHashCode(r), r.intent.getAction(), logIndex, ri.toString());
}
} else {
- Slog.w(TAG, "Discarding broadcast before first receiver is invoked: "
- + r);
+ if (logIndex < 0) Slog.w(TAG,
+ "Discarding broadcast before first receiver is invoked: " + r);
EventLog.writeEvent(EventLogTags.AM_BROADCAST_DISCARD_APP,
-1, System.identityHashCode(r),
r.intent.getAction(),
diff --git a/services/core/java/com/android/server/connectivity/NetworkMonitor.java b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
index bf896a5..fc50e2c 100644
--- a/services/core/java/com/android/server/connectivity/NetworkMonitor.java
+++ b/services/core/java/com/android/server/connectivity/NetworkMonitor.java
@@ -32,6 +32,7 @@
import android.net.wifi.WifiManager;
import android.os.Handler;
import android.os.Message;
+import android.os.Process;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UserHandle;
@@ -48,6 +49,7 @@
import android.telephony.TelephonyManager;
import android.util.Log;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.Protocol;
import com.android.internal.util.State;
import com.android.internal.util.StateMachine;
@@ -177,8 +179,8 @@
private static final int CMD_LAUNCH_CAPTIVE_PORTAL_APP = BASE + 11;
private static final String LINGER_DELAY_PROPERTY = "persist.netmon.linger";
- // Default to 30s linger time-out.
- private static final int DEFAULT_LINGER_DELAY_MS = 30000;
+ // Default to 30s linger time-out. Modifyable only for testing.
+ private static int DEFAULT_LINGER_DELAY_MS = 30000;
private final int mLingerDelayMs;
private int mLingerToken = 0;
@@ -771,4 +773,13 @@
mContext.sendBroadcastAsUser(latencyBroadcast, UserHandle.CURRENT,
PERMISSION_ACCESS_NETWORK_CONDITIONS);
}
+
+ // Allow tests to override linger time.
+ @VisibleForTesting
+ public static void SetDefaultLingerTime(int time_ms) {
+ if (Process.myUid() == Process.SYSTEM_UID) {
+ throw new SecurityException("SetDefaultLingerTime only for internal testing.");
+ }
+ DEFAULT_LINGER_DELAY_MS = time_ms;
+ }
}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index dcf668d..1cec750 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -52,6 +52,7 @@
import android.os.ParcelFileDescriptor;
import android.os.Process;
import android.os.RemoteException;
+import android.os.SELinux;
import android.os.UserHandle;
import android.system.ErrnoException;
import android.system.Os;
@@ -77,6 +78,7 @@
import java.io.FileDescriptor;
import java.io.IOException;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
@@ -163,6 +165,8 @@
private final List<File> mResolvedStagedFiles = new ArrayList<>();
@GuardedBy("mLock")
private final List<File> mResolvedInheritedFiles = new ArrayList<>();
+ @GuardedBy("mLock")
+ private File mInheritedFilesBase;
private final Handler.Callback mHandlerCallback = new Handler.Callback() {
@Override
@@ -511,8 +515,13 @@
final List<File> fromFiles = mResolvedInheritedFiles;
final File toDir = resolveStageDir();
+ if (LOGD) Slog.d(TAG, "Inherited files: " + mResolvedInheritedFiles);
+ if (!mResolvedInheritedFiles.isEmpty() && mInheritedFilesBase == null) {
+ throw new IllegalStateException("mInheritedFilesBase == null");
+ }
+
if (isLinkPossible(fromFiles, toDir)) {
- linkFiles(fromFiles, toDir);
+ createDirsAndLinkFiles(fromFiles, toDir, mInheritedFilesBase);
} else {
// TODO: this should delegate to DCS so the system process
// avoids holding open FDs into containers.
@@ -690,6 +699,31 @@
}
}
}
+
+ // Inherit compiled oat directory.
+ final File packageInstallDir = (new File(app.getBaseCodePath())).getParentFile();
+ mInheritedFilesBase = packageInstallDir;
+ final File oatDir = new File(packageInstallDir, "oat");
+ if (oatDir.exists()) {
+ final File[] archSubdirs = oatDir.listFiles();
+ // Only add "oatDir" if it contains arch specific subdirs.
+ if (archSubdirs != null && archSubdirs.length > 0) {
+ mResolvedInheritedFiles.add(oatDir);
+ }
+ final String[] instructionSets = InstructionSets.getAllDexCodeInstructionSets();
+ for (File archSubDir : archSubdirs) {
+ // Skip any directory that isn't an ISA subdir.
+ if (!ArrayUtils.contains(instructionSets, archSubDir.getName())) {
+ continue;
+ }
+
+ List<File> oatFiles = Arrays.asList(archSubDir.listFiles());
+ if (!oatFiles.isEmpty()) {
+ mResolvedInheritedFiles.add(archSubDir);
+ mResolvedInheritedFiles.addAll(oatFiles);
+ }
+ }
+ }
}
}
@@ -768,14 +802,69 @@
return true;
}
- private static void linkFiles(List<File> fromFiles, File toDir) throws IOException {
+ /**
+ * Reparents the path of {@code file} from {@code oldBase} to {@code newBase}. {@code file}
+ * must necessarily be a subpath of {@code oldBase}. It is an error for {@code file} to have
+ * relative path components such as {@code "."} or {@code ".."}. For example, for we will
+ * reparent {@code /foo/bar/baz} to {@code /foo2/bar/baz} if {@code oldBase} was {@code /foo}
+ * and {@code newBase} was {@code /foo2}.
+ */
+ private static File reparentPath(File file, File oldBase, File newBase) throws IOException {
+ final String oldBaseStr = oldBase.getAbsolutePath();
+ final String pathStr = file.getAbsolutePath();
+
+ // Don't allow relative paths.
+ if (pathStr.contains("/.") ) {
+ throw new IOException("Invalid path (was relative) : " + pathStr);
+ }
+
+ if (pathStr.startsWith(oldBaseStr)) {
+ final String relative = pathStr.substring(oldBaseStr.length());
+ return new File(newBase, relative);
+ }
+
+ throw new IOException("File: " + pathStr + " outside base: " + oldBaseStr);
+ }
+
+ /**
+ * Recreates a directory and file structure, specified by a list of files {@code fromFiles}
+ * which are subpaths of {@code fromDir} to {@code toDir}. Directories are created with the
+ * same permissions, and regular files are linked.
+ *
+ * TODO: Move this function to installd so that the system process doesn't have to
+ * manipulate / relabel directories.
+ */
+ private static void createDirsAndLinkFiles(List<File> fromFiles, File toDir, File fromDir)
+ throws IOException {
for (File fromFile : fromFiles) {
- final File toFile = new File(toDir, fromFile.getName());
+ final File toFile = reparentPath(fromFile, fromDir, toDir);
+ final StructStat stat;
try {
- if (LOGD) Slog.d(TAG, "Linking " + fromFile + " to " + toFile);
- Os.link(fromFile.getAbsolutePath(), toFile.getAbsolutePath());
+ stat = Os.stat(fromFile.getAbsolutePath());
} catch (ErrnoException e) {
- throw new IOException("Failed to link " + fromFile + " to " + toFile, e);
+ throw new IOException("Failed to stat: " + fromFile.getAbsolutePath(), e);
+ }
+
+ if (OsConstants.S_ISDIR(stat.st_mode)) {
+ if (LOGD) Slog.d(TAG, "Creating directory " + toFile.getAbsolutePath());
+ try {
+ Os.mkdir(toFile.getAbsolutePath(), stat.st_mode);
+ } catch (ErrnoException e) {
+ throw new IOException("Failed to create dir: " + toFile.getAbsolutePath(), e);
+ }
+
+ // We do this to ensure that the oat/ directory is created with the right
+ // label (data_dalvikcache_file) instead of apk_tmpfile.
+ if (!SELinux.restorecon(toFile)) {
+ throw new IOException("Failed to restorecon: " + toFile.getAbsolutePath());
+ }
+ } else {
+ if (LOGD) Slog.d(TAG, "Linking " + fromFile + " to " + toFile);
+ try {
+ Os.link(fromFile.getAbsolutePath(), toFile.getAbsolutePath());
+ } catch (ErrnoException e) {
+ throw new IOException("Failed to link " + fromFile + " to " + toFile, e);
+ }
}
}
Slog.d(TAG, "Linked " + fromFiles.size() + " files into " + toDir);
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 47e3963..e370afc 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -1876,7 +1876,7 @@
alreadyDexOpted.add(frameworkDir.getPath() + "/core-libart.jar");
/**
- * And there are a number of commands implemented in Java, which
+ * There are a number of commands implemented in Java, which
* we currently need to do the dexopt on so that they can be
* run from a non-root shell.
*/
@@ -6361,63 +6361,39 @@
final String path = scanFile.getPath();
final String cpuAbiOverride = deriveAbiOverride(pkg.cpuAbiOverride, pkgSetting);
- if (isSystemApp(pkg) && !pkg.isUpdatedSystemApp()) {
- setBundledAppAbisAndRoots(pkg, pkgSetting);
- // If we haven't found any native libraries for the app, check if it has
- // renderscript code. We'll need to force the app to 32 bit if it has
- // renderscript bitcode.
- if (pkg.applicationInfo.primaryCpuAbi == null
- && pkg.applicationInfo.secondaryCpuAbi == null
- && Build.SUPPORTED_64_BIT_ABIS.length > 0) {
- NativeLibraryHelper.Handle handle = null;
- try {
- handle = NativeLibraryHelper.Handle.create(scanFile);
- if (NativeLibraryHelper.hasRenderscriptBitcode(handle)) {
- pkg.applicationInfo.primaryCpuAbi = Build.SUPPORTED_32_BIT_ABIS[0];
- }
- } catch (IOException ioe) {
- Slog.w(TAG, "Error scanning system app : " + ioe);
- } finally {
- IoUtils.closeQuietly(handle);
- }
- }
-
- setNativeLibraryPaths(pkg);
+ if ((scanFlags & SCAN_NEW_INSTALL) == 0) {
+ derivePackageAbi(pkg, scanFile, cpuAbiOverride, true /* extract libs */);
} else {
- if ((scanFlags & SCAN_NEW_INSTALL) == 0) {
- deriveNonSystemPackageAbi(pkg, scanFile, cpuAbiOverride, true /* extract libs */);
- } else {
- if ((scanFlags & SCAN_MOVE) != 0) {
- // We haven't run dex-opt for this move (since we've moved the compiled output too)
- // but we already have this packages package info in the PackageSetting. We just
- // use that and derive the native library path based on the new codepath.
- pkg.applicationInfo.primaryCpuAbi = pkgSetting.primaryCpuAbiString;
- pkg.applicationInfo.secondaryCpuAbi = pkgSetting.secondaryCpuAbiString;
- }
-
- // Set native library paths again. For moves, the path will be updated based on the
- // ABIs we've determined above. For non-moves, the path will be updated based on the
- // ABIs we determined during compilation, but the path will depend on the final
- // package path (after the rename away from the stage path).
- setNativeLibraryPaths(pkg);
+ if ((scanFlags & SCAN_MOVE) != 0) {
+ // We haven't run dex-opt for this move (since we've moved the compiled output too)
+ // but we already have this packages package info in the PackageSetting. We just
+ // use that and derive the native library path based on the new codepath.
+ pkg.applicationInfo.primaryCpuAbi = pkgSetting.primaryCpuAbiString;
+ pkg.applicationInfo.secondaryCpuAbi = pkgSetting.secondaryCpuAbiString;
}
- if (DEBUG_INSTALL) Slog.i(TAG, "Linking native library dir for " + path);
- final int[] userIds = sUserManager.getUserIds();
- synchronized (mInstallLock) {
- // Create a native library symlink only if we have native libraries
- // and if the native libraries are 32 bit libraries. We do not provide
- // this symlink for 64 bit libraries.
- if (pkg.applicationInfo.primaryCpuAbi != null &&
- !VMRuntime.is64BitAbi(pkg.applicationInfo.primaryCpuAbi)) {
- final String nativeLibPath = pkg.applicationInfo.nativeLibraryDir;
- for (int userId : userIds) {
- if (mInstaller.linkNativeLibraryDirectory(pkg.volumeUuid, pkg.packageName,
- nativeLibPath, userId) < 0) {
- throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
- "Failed linking native library dir (user=" + userId + ")");
- }
+ // Set native library paths again. For moves, the path will be updated based on the
+ // ABIs we've determined above. For non-moves, the path will be updated based on the
+ // ABIs we determined during compilation, but the path will depend on the final
+ // package path (after the rename away from the stage path).
+ setNativeLibraryPaths(pkg);
+ }
+
+ if (DEBUG_INSTALL) Slog.i(TAG, "Linking native library dir for " + path);
+ final int[] userIds = sUserManager.getUserIds();
+ synchronized (mInstallLock) {
+ // Create a native library symlink only if we have native libraries
+ // and if the native libraries are 32 bit libraries. We do not provide
+ // this symlink for 64 bit libraries.
+ if (pkg.applicationInfo.primaryCpuAbi != null &&
+ !VMRuntime.is64BitAbi(pkg.applicationInfo.primaryCpuAbi)) {
+ final String nativeLibPath = pkg.applicationInfo.nativeLibraryDir;
+ for (int userId : userIds) {
+ if (mInstaller.linkNativeLibraryDirectory(pkg.volumeUuid, pkg.packageName,
+ nativeLibPath, userId) < 0) {
+ throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
+ "Failed linking native library dir (user=" + userId + ")");
}
}
}
@@ -6946,8 +6922,8 @@
*
* If {@code extractLibs} is true, native libraries are extracted from the app if required.
*/
- public void deriveNonSystemPackageAbi(PackageParser.Package pkg, File scanFile,
- String cpuAbiOverride, boolean extractLibs)
+ public void derivePackageAbi(PackageParser.Package pkg, File scanFile,
+ String cpuAbiOverride, boolean extractLibs)
throws PackageManagerException {
// TODO: We can probably be smarter about this stuff. For installed apps,
// we can calculate this information at install time once and for all. For
@@ -6959,8 +6935,10 @@
setNativeLibraryPaths(pkg);
// We would never need to extract libs for forward-locked and external packages,
- // since the container service will do it for us.
- if (pkg.isForwardLocked() || isExternal(pkg)) {
+ // since the container service will do it for us. We shouldn't attempt to
+ // extract libs from system app when it was not updated.
+ if (pkg.isForwardLocked() || isExternal(pkg) ||
+ (isSystemApp(pkg) && !pkg.isUpdatedSystemApp()) ) {
extractLibs = false;
}
@@ -7291,33 +7269,6 @@
}
/**
- * Calculate the abis and roots for a bundled app. These can uniquely
- * be determined from the contents of the system partition, i.e whether
- * it contains 64 or 32 bit shared libraries etc. We do not validate any
- * of this information, and instead assume that the system was built
- * sensibly.
- */
- private void setBundledAppAbisAndRoots(PackageParser.Package pkg,
- PackageSetting pkgSetting) {
- final String apkName = deriveCodePathName(pkg.applicationInfo.getCodePath());
-
- // If "/system/lib64/apkname" exists, assume that is the per-package
- // native library directory to use; otherwise use "/system/lib/apkname".
- final String apkRoot = calculateBundledApkRoot(pkg.applicationInfo.sourceDir);
- setBundledAppAbi(pkg, apkRoot, apkName);
- // pkgSetting might be null during rescan following uninstall of updates
- // to a bundled app, so accommodate that possibility. The settings in
- // that case will be established later from the parsed package.
- //
- // If the settings aren't null, sync them up with what we've just derived.
- // note that apkRoot isn't stored in the package settings.
- if (pkgSetting != null) {
- pkgSetting.primaryCpuAbiString = pkg.applicationInfo.primaryCpuAbi;
- pkgSetting.secondaryCpuAbiString = pkg.applicationInfo.secondaryCpuAbi;
- }
- }
-
- /**
* Deduces the ABI of a bundled app and sets the relevant fields on the
* parsed pkg object.
*
@@ -11504,7 +11455,7 @@
final boolean onExternal = (((installFlags & PackageManager.INSTALL_EXTERNAL) != 0)
|| (args.volumeUuid != null));
boolean replace = false;
- int scanFlags = SCAN_NEW_INSTALL | SCAN_FORCE_DEX | SCAN_UPDATE_SIGNATURE;
+ int scanFlags = SCAN_NEW_INSTALL | SCAN_UPDATE_SIGNATURE;
// Result object to be returned
res.returnCode = PackageManager.INSTALL_SUCCEEDED;
@@ -11695,7 +11646,7 @@
scanFlags |= SCAN_NO_DEX;
try {
- deriveNonSystemPackageAbi(pkg, new File(pkg.codePath), args.abiOverride,
+ derivePackageAbi(pkg, new File(pkg.codePath), args.abiOverride,
true /* extract libs */);
} catch (PackageManagerException pme) {
Slog.e(TAG, "Error deriving application ABI", pme);
@@ -11705,7 +11656,7 @@
// Run dexopt before old package gets removed, to minimize time when app is unavailable
int result = mPackageDexOptimizer
- .performDexOpt(pkg, null /* instruction sets */, true /* forceDex */,
+ .performDexOpt(pkg, null /* instruction sets */, false /* forceDex */,
false /* defer */, false /* inclDependencies */);
if (result == PackageDexOptimizer.DEX_OPT_FAILED) {
res.setError(INSTALL_FAILED_DEXOPT, "Dexopt failed for " + pkg.codePath);
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index b8d0692..46793b9 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -1087,16 +1087,19 @@
}
}
- private void sleepPress(KeyEvent event) {
+ private void sleepPress(long eventTime) {
+ if (mShortPressOnSleepBehavior == SHORT_PRESS_SLEEP_GO_TO_SLEEP_AND_GO_HOME) {
+ launchHomeFromHotKey(false /* awakenDreams */, true /*respectKeyguard*/);
+ }
+ }
+
+ private void sleepRelease(long eventTime) {
switch (mShortPressOnSleepBehavior) {
case SHORT_PRESS_SLEEP_GO_TO_SLEEP:
- mPowerManager.goToSleep(event.getEventTime(),
- PowerManager.GO_TO_SLEEP_REASON_SLEEP_BUTTON, 0);
- break;
case SHORT_PRESS_SLEEP_GO_TO_SLEEP_AND_GO_HOME:
- launchHomeFromHotKey(false /* awakenDreams */, true /*respectKeyguard*/);
- mPowerManager.goToSleep(event.getEventTime(),
- PowerManager.GO_TO_SLEEP_REASON_SLEEP_BUTTON, 0);
+ Slog.i(TAG, "sleepRelease() calling goToSleep(GO_TO_SLEEP_REASON_SLEEP_BUTTON)");
+ mPowerManager.goToSleep(eventTime,
+ PowerManager.GO_TO_SLEEP_REASON_SLEEP_BUTTON, 0);
break;
}
}
@@ -2116,7 +2119,9 @@
case TYPE_KEYGUARD_SCRIM:
return false;
default:
- return true;
+ // Hide only windows below the keyguard host window.
+ return windowTypeToLayerLw(win.getBaseType())
+ < windowTypeToLayerLw(TYPE_STATUS_BAR);
}
}
@@ -4903,7 +4908,11 @@
if (!mPowerManager.isInteractive()) {
useHapticFeedback = false; // suppress feedback if already non-interactive
}
- sleepPress(event);
+ if (down) {
+ sleepPress(event.getEventTime());
+ } else {
+ sleepRelease(event.getEventTime());
+ }
break;
}
@@ -5517,6 +5526,12 @@
/** {@inheritDoc} */
@Override
+ public boolean isKeyguardShowingOrOccluded() {
+ return mKeyguardDelegate == null ? false : mKeyguardDelegate.isShowing();
+ }
+
+ /** {@inheritDoc} */
+ @Override
public boolean inKeyguardRestrictedKeyInputMode() {
if (mKeyguardDelegate == null) return false;
return mKeyguardDelegate.isInputRestricted();
diff --git a/services/core/java/com/android/server/policy/keyguard/KeyguardStateMonitor.java b/services/core/java/com/android/server/policy/keyguard/KeyguardStateMonitor.java
index 01c110f..f1f9c50 100644
--- a/services/core/java/com/android/server/policy/keyguard/KeyguardStateMonitor.java
+++ b/services/core/java/com/android/server/policy/keyguard/KeyguardStateMonitor.java
@@ -36,10 +36,11 @@
// Keyguard changes its state, it always triggers a layout in window manager. Because
// IKeyguardStateCallback is synchronous and because these states are declared volatile, it's
// guaranteed that window manager picks up the new state all the time in the layout caused by
- // the state change of Keyguard.
- private volatile boolean mIsShowing;
- private volatile boolean mSimSecure;
- private volatile boolean mInputRestricted;
+ // the state change of Keyguard. To be extra safe, assume most restrictive values until Keyguard
+ // tells us the actual value.
+ private volatile boolean mIsShowing = true;
+ private volatile boolean mSimSecure = true;
+ private volatile boolean mInputRestricted = true;
private int mCurrentUserId;
diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java
index 9169351..3305e1e 100644
--- a/services/core/java/com/android/server/wm/WindowAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowAnimator.java
@@ -191,7 +191,8 @@
private boolean shouldForceHide(WindowState win) {
final WindowState imeTarget = mService.mInputMethodTarget;
final boolean showImeOverKeyguard = imeTarget != null && imeTarget.isVisibleNow() &&
- (imeTarget.getAttrs().flags & FLAG_SHOW_WHEN_LOCKED) != 0;
+ ((imeTarget.getAttrs().flags & FLAG_SHOW_WHEN_LOCKED) != 0
+ || !mPolicy.canBeForceHidden(imeTarget, imeTarget.mAttrs));
final WindowState winShowWhenLocked = (WindowState) mPolicy.getWinShowWhenLockedLw();
final AppWindowToken appShowWhenLocked = winShowWhenLocked == null ?
@@ -203,7 +204,11 @@
|| (win.mAttrs.flags & FLAG_SHOW_WHEN_LOCKED) != 0 && win.isAnimatingLw()
// Show error dialogs over apps that dismiss keyguard.
|| (win.mAttrs.privateFlags & PRIVATE_FLAG_SYSTEM_ERROR) != 0)));
- return (mForceHiding == KEYGUARD_SHOWN) && hideWhenLocked;
+
+ // Only hide windows if the keyguard is active and not animating away.
+ boolean keyguardOn = mPolicy.isKeyguardShowingOrOccluded()
+ && mForceHiding != KEYGUARD_ANIMATING_OUT;
+ return keyguardOn && hideWhenLocked;
}
private void updateWindowsLocked(final int displayId) {
@@ -228,6 +233,7 @@
winAnimator.mAnimation.setDuration(KEYGUARD_ANIM_TIMEOUT_MS);
winAnimator.mAnimationIsEntrance = false;
winAnimator.mAnimationStartTime = -1;
+ winAnimator.mKeyguardGoingAwayAnimation = true;
}
} else {
if (DEBUG_KEYGUARD) Slog.d(TAG,
@@ -310,7 +316,7 @@
mKeyguardGoingAway = false;
}
if (win.isReadyForDisplay()) {
- if (nowAnimating) {
+ if (nowAnimating && win.mWinAnimator.mKeyguardGoingAwayAnimation) {
mForceHiding = KEYGUARD_ANIMATING_OUT;
} else {
mForceHiding = win.isDrawnLw() ? KEYGUARD_SHOWN : KEYGUARD_NOT_SHOWN;
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 7019453..6b5c224 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2734,14 +2734,16 @@
}
}
final AppWindowToken appToken = win.mAppToken;
+ // Prevent an immediate window exit only for a real animation, ignoring e.g.
+ // dummy animations.
+ final boolean inAnimation = win.mWinAnimator.isWindowAnimatingNow();
// The starting window is the last window in this app token and it isn't animating.
// Allow it to be removed now as there is no additional window or animation that will
// trigger its removal.
final boolean lastWinStartingNotAnimating = startingWindow && appToken!= null
- && appToken.allAppWindows.size() == 1 && !win.mWinAnimator.isWindowAnimating();
- if (!lastWinStartingNotAnimating && (win.mExiting || win.mWinAnimator.isAnimating())) {
+ && appToken.allAppWindows.size() == 1 && !inAnimation;
+ if (!lastWinStartingNotAnimating && (win.mExiting || inAnimation)) {
// The exit animation is running... wait for it!
- //Slog.i(TAG, "*** Running exit animation...");
win.mExiting = true;
win.mRemoveOnExit = true;
final DisplayContent displayContent = win.getDisplayContent();
@@ -2757,7 +2759,6 @@
if (focusChanged) {
mInputMonitor.updateInputWindowsLw(false /*force*/);
}
- //dump();
Binder.restoreCallingIdentity(origId);
return;
}
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index e9023fd..b42c8eb 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -251,11 +251,21 @@
&& mAppAnimator.animation == AppWindowAnimator.sDummyAnimation;
}
- /** Is this window currently animating? */
+ /** Is this window currently set to animate or currently animating?
+ * NOTE: The method will return true for cases where the window isn't currently animating, but
+ * is set to animate. i.e. if the window animation is currently set to a dummy placeholder
+ * animation. Use {@link #isWindowAnimatingNow} to know if the window is currently running a
+ * real animation. */
boolean isWindowAnimating() {
return mAnimation != null;
}
+ /** Is the window performing a real animation and not a dummy which is only waiting for an
+ * an animation to start? */
+ boolean isWindowAnimatingNow() {
+ return isWindowAnimating() && !isDummyAnimation();
+ }
+
void cancelExitAnimationForNextAnimationLocked() {
if (mAnimation != null) {
mAnimation.cancel();
diff --git a/services/net/java/android/net/dhcp/DhcpAckPacket.java b/services/net/java/android/net/dhcp/DhcpAckPacket.java
index c0e1d19..334f708 100644
--- a/services/net/java/android/net/dhcp/DhcpAckPacket.java
+++ b/services/net/java/android/net/dhcp/DhcpAckPacket.java
@@ -30,8 +30,8 @@
private final Inet4Address mSrcIp;
DhcpAckPacket(int transId, short secs, boolean broadcast, Inet4Address serverAddress,
- Inet4Address clientIp, byte[] clientMac) {
- super(transId, secs, INADDR_ANY, clientIp, serverAddress, INADDR_ANY, clientMac, broadcast);
+ Inet4Address clientIp, Inet4Address yourIp, byte[] clientMac) {
+ super(transId, secs, clientIp, yourIp, serverAddress, INADDR_ANY, clientMac, broadcast);
mBroadcast = broadcast;
mSrcIp = serverAddress;
}
diff --git a/services/net/java/android/net/dhcp/DhcpClient.java b/services/net/java/android/net/dhcp/DhcpClient.java
index d8d74e2..575a300 100644
--- a/services/net/java/android/net/dhcp/DhcpClient.java
+++ b/services/net/java/android/net/dhcp/DhcpClient.java
@@ -58,7 +58,7 @@
import java.util.Arrays;
import java.util.Random;
-import libcore.io.IoUtils;
+import libcore.io.IoBridge;
import static android.system.OsConstants.*;
import static android.net.dhcp.DhcpPacket.*;
@@ -297,9 +297,15 @@
return true;
}
+ private static void closeQuietly(FileDescriptor fd) {
+ try {
+ IoBridge.closeAndSignalBlockedThreads(fd);
+ } catch (IOException ignored) {}
+ }
+
private void closeSockets() {
- IoUtils.closeQuietly(mUdpSock);
- IoUtils.closeQuietly(mPacketSock);
+ closeQuietly(mUdpSock);
+ closeQuietly(mPacketSock);
}
private boolean setIpAddress(LinkAddress address) {
@@ -326,7 +332,7 @@
@Override
public void run() {
- maybeLog("Starting receive thread");
+ maybeLog("Receive thread started");
while (!stopped) {
try {
int length = Os.read(mPacketSock, mPacket, 0, mPacket.length);
@@ -345,7 +351,7 @@
}
}
}
- maybeLog("Stopping receive thread");
+ maybeLog("Receive thread stopped");
}
}
@@ -463,6 +469,8 @@
return "CMD_KICK";
case CMD_RECEIVED_PACKET:
return "CMD_RECEIVED_PACKET";
+ case CMD_TIMEOUT:
+ return "CMD_TIMEOUT";
default:
return Integer.toString(what);
}
@@ -601,12 +609,15 @@
/**
* Retransmits packets using jittered exponential backoff with an optional timeout. Packet
- * transmission is triggered by CMD_KICK, which is sent by an AlarmManager alarm.
+ * transmission is triggered by CMD_KICK, which is sent by an AlarmManager alarm. If a subclass
+ * sets mTimeout to a positive value, then timeout() is called by an AlarmManager alarm mTimeout
+ * milliseconds after entering the state. Kicks and timeouts are cancelled when leaving the
+ * state.
*
* Concrete subclasses must implement sendPacket, which is called when the alarm fires and a
* packet needs to be transmitted, and receivePacket, which is triggered by CMD_RECEIVED_PACKET
- * sent by the receive thread. They may implement timeout, which is called when the timeout
- * fires.
+ * sent by the receive thread. They may also set mTimeout and if desired override the default
+ * timeout implementation.
*/
abstract class PacketRetransmittingState extends LoggingState {
@@ -647,7 +658,19 @@
abstract protected boolean sendPacket();
abstract protected void receivePacket(DhcpPacket packet);
- protected void timeout() {}
+
+ // Default implementation of timeout. This is only invoked if mTimeout > 0, so it will never
+ // be called if the subclass does not set a timeout.
+ protected void timeout() {
+ maybeLog("Timeout in " + getName());
+ notifyFailure();
+ if (this != mDhcpInitState) {
+ // Only transition to INIT if we're not already there. Otherwise, we'll exit the
+ // state and re-enter it, which will reset the packet transmission interval, re-set
+ // the timeout, etc.
+ transitionTo(mDhcpInitState);
+ }
+ }
protected void initTimer() {
mTimer = FIRST_TIMEOUT_MS;
@@ -696,11 +719,6 @@
return sendDiscoverPacket();
}
- protected void timeout() {
- maybeLog("Timeout");
- notifyFailure();
- }
-
protected void receivePacket(DhcpPacket packet) {
if (!isValidPacket(packet)) return;
if (!(packet instanceof DhcpOfferPacket)) return;
@@ -747,11 +765,6 @@
transitionTo(mDhcpInitState);
}
}
-
- protected void timeout() {
- notifyFailure();
- transitionTo(mDhcpInitState);
- }
}
class DhcpHaveAddressState extends LoggingState {
@@ -760,6 +773,8 @@
super.enter();
if (!setIpAddress(mDhcpLease.ipAddress)) {
notifyFailure();
+ // There's likely no point in going into DhcpInitState here, we'll probably just
+ // repeat the transaction, get the same IP address as before, and fail.
transitionTo(mStoppedState);
}
}
@@ -797,7 +812,6 @@
}
}
- // TODO: timeout.
class DhcpRenewingState extends PacketRetransmittingState {
public DhcpRenewingState() {
super();
diff --git a/services/net/java/android/net/dhcp/DhcpOfferPacket.java b/services/net/java/android/net/dhcp/DhcpOfferPacket.java
index af41708..7ca7100 100644
--- a/services/net/java/android/net/dhcp/DhcpOfferPacket.java
+++ b/services/net/java/android/net/dhcp/DhcpOfferPacket.java
@@ -32,8 +32,8 @@
* Generates a OFFER packet with the specified parameters.
*/
DhcpOfferPacket(int transId, short secs, boolean broadcast, Inet4Address serverAddress,
- Inet4Address clientIp, byte[] clientMac) {
- super(transId, secs, INADDR_ANY, clientIp, INADDR_ANY, INADDR_ANY, clientMac, broadcast);
+ Inet4Address clientIp, Inet4Address yourIp, byte[] clientMac) {
+ super(transId, secs, clientIp, yourIp, INADDR_ANY, INADDR_ANY, clientMac, broadcast);
mSrcIp = serverAddress;
}
diff --git a/services/net/java/android/net/dhcp/DhcpPacket.java b/services/net/java/android/net/dhcp/DhcpPacket.java
index 2a25d30..d42404b 100644
--- a/services/net/java/android/net/dhcp/DhcpPacket.java
+++ b/services/net/java/android/net/dhcp/DhcpPacket.java
@@ -919,7 +919,7 @@
break;
case DHCP_MESSAGE_TYPE_OFFER:
newPacket = new DhcpOfferPacket(
- transactionId, secs, broadcast, ipSrc, yourIp, clientMac);
+ transactionId, secs, broadcast, ipSrc, clientIp, yourIp, clientMac);
break;
case DHCP_MESSAGE_TYPE_REQUEST:
newPacket = new DhcpRequestPacket(
@@ -932,7 +932,7 @@
break;
case DHCP_MESSAGE_TYPE_ACK:
newPacket = new DhcpAckPacket(
- transactionId, secs, broadcast, ipSrc, yourIp, clientMac);
+ transactionId, secs, broadcast, ipSrc, clientIp, yourIp, clientMac);
break;
case DHCP_MESSAGE_TYPE_NAK:
newPacket = new DhcpNakPacket(
@@ -982,9 +982,9 @@
*/
public DhcpResults toDhcpResults() {
Inet4Address ipAddress = mYourIp;
- if (ipAddress == Inet4Address.ANY) {
+ if (ipAddress.equals(Inet4Address.ANY)) {
ipAddress = mClientIp;
- if (ipAddress == Inet4Address.ANY) {
+ if (ipAddress.equals(Inet4Address.ANY)) {
return null;
}
}
@@ -1052,7 +1052,7 @@
Inet4Address gateway, List<Inet4Address> dnsServers,
Inet4Address dhcpServerIdentifier, String domainName) {
DhcpPacket pkt = new DhcpOfferPacket(
- transactionId, (short) 0, broadcast, serverIpAddr, clientIpAddr, mac);
+ transactionId, (short) 0, broadcast, serverIpAddr, INADDR_ANY, clientIpAddr, mac);
pkt.mGateway = gateway;
pkt.mDnsServers = dnsServers;
pkt.mLeaseTime = timeout;
@@ -1072,7 +1072,7 @@
Inet4Address gateway, List<Inet4Address> dnsServers,
Inet4Address dhcpServerIdentifier, String domainName) {
DhcpPacket pkt = new DhcpAckPacket(
- transactionId, (short) 0, broadcast, serverIpAddr, clientIpAddr, mac);
+ transactionId, (short) 0, broadcast, serverIpAddr, INADDR_ANY, clientIpAddr, mac);
pkt.mGateway = gateway;
pkt.mDnsServers = dnsServers;
pkt.mLeaseTime = timeout;
diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml
index 636dd4d..919293a 100644
--- a/services/tests/servicestests/AndroidManifest.xml
+++ b/services/tests/servicestests/AndroidManifest.xml
@@ -36,6 +36,9 @@
<uses-permission android:name="android.permission.MANAGE_USERS" />
<uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
<uses-permission android:name="android.permission.MANAGE_DEVICE_ADMINS" />
+ <uses-permission android:name="android.permission.MODIFY_PHONE_STATE" />
+ <uses-permission android:name="android.permission.INTERNET" />
+ <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<application>
<uses-library android:name="android.test.runner" />
diff --git a/services/tests/servicestests/src/android/net/dhcp/DhcpPacketTest.java b/services/tests/servicestests/src/android/net/dhcp/DhcpPacketTest.java
index f116042..e0e3fcf 100644
--- a/services/tests/servicestests/src/android/net/dhcp/DhcpPacketTest.java
+++ b/services/tests/servicestests/src/android/net/dhcp/DhcpPacketTest.java
@@ -18,6 +18,7 @@
import android.net.NetworkUtils;
import android.net.DhcpResults;
+import android.net.LinkAddress;
import android.system.OsConstants;
import android.test.suitebuilder.annotation.SmallTest;
import junit.framework.TestCase;
@@ -34,19 +35,27 @@
(Inet4Address) NetworkUtils.numericToInetAddress("192.0.2.1");
private static Inet4Address CLIENT_ADDR =
(Inet4Address) NetworkUtils.numericToInetAddress("192.0.2.234");
+ // Use our own empty address instead of Inet4Address.ANY or INADDR_ANY to ensure that the code
+ // doesn't use == instead of equals when comparing addresses.
+ private static Inet4Address ANY = (Inet4Address) NetworkUtils.numericToInetAddress("0.0.0.0");
+
private static byte[] CLIENT_MAC = new byte[] { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05 };
class TestDhcpPacket extends DhcpPacket {
private byte mType;
// TODO: Make this a map of option numbers to bytes instead.
- private byte[] mDomainBytes, mVendorInfoBytes, mLeaseTimeBytes;
+ private byte[] mDomainBytes, mVendorInfoBytes, mLeaseTimeBytes, mNetmaskBytes;
- public TestDhcpPacket(byte type) {
- super(0xdeadbeef, (short) 0, INADDR_ANY, CLIENT_ADDR, INADDR_ANY, INADDR_ANY,
+ public TestDhcpPacket(byte type, Inet4Address clientIp, Inet4Address yourIp) {
+ super(0xdeadbeef, (short) 0, clientIp, yourIp, INADDR_ANY, INADDR_ANY,
CLIENT_MAC, true);
mType = type;
}
+ public TestDhcpPacket(byte type) {
+ this(type, INADDR_ANY, CLIENT_ADDR);
+ }
+
public TestDhcpPacket setDomainBytes(byte[] domainBytes) {
mDomainBytes = domainBytes;
return this;
@@ -62,6 +71,11 @@
return this;
}
+ public TestDhcpPacket setNetmaskBytes(byte[] netmaskBytes) {
+ mNetmaskBytes = netmaskBytes;
+ return this;
+ }
+
public ByteBuffer buildPacket(int encap, short unusedDestUdp, short unusedSrcUdp) {
ByteBuffer result = ByteBuffer.allocate(MAX_LENGTH);
fillInPacket(encap, CLIENT_ADDR, SERVER_ADDR,
@@ -80,6 +94,9 @@
if (mLeaseTimeBytes != null) {
addTlv(buffer, DHCP_LEASE_TIME, mLeaseTimeBytes);
}
+ if (mNetmaskBytes != null) {
+ addTlv(buffer, DHCP_SUBNET_MASK, mNetmaskBytes);
+ }
addTlvEnd(buffer);
}
@@ -175,4 +192,50 @@
assertLeaseTimeParses(true, -2147483647, 2147483649L * 1000, maxIntPlusOneLease);
assertLeaseTimeParses(true, DhcpPacket.INFINITE_LEASE, 0, infiniteLease);
}
+
+ private void checkIpAddress(String expected, Inet4Address clientIp, Inet4Address yourIp,
+ byte[] netmaskBytes) {
+ checkIpAddress(expected, DHCP_MESSAGE_TYPE_OFFER, clientIp, yourIp, netmaskBytes);
+ checkIpAddress(expected, DHCP_MESSAGE_TYPE_ACK, clientIp, yourIp, netmaskBytes);
+ }
+
+ private void checkIpAddress(String expected, byte type,
+ Inet4Address clientIp, Inet4Address yourIp,
+ byte[] netmaskBytes) {
+ ByteBuffer packet = new TestDhcpPacket(type, clientIp, yourIp)
+ .setNetmaskBytes(netmaskBytes)
+ .build();
+ DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_BOOTP);
+ DhcpResults results = offerPacket.toDhcpResults();
+
+ if (expected != null) {
+ LinkAddress expectedAddress = new LinkAddress(expected);
+ assertEquals(expectedAddress, results.ipAddress);
+ } else {
+ assertNull(results);
+ }
+ }
+
+ @SmallTest
+ public void testIpAddress() throws Exception {
+ byte[] slash11Netmask = new byte[] { (byte) 0xff, (byte) 0xe0, 0x00, 0x00 };
+ byte[] slash24Netmask = new byte[] { (byte) 0xff, (byte) 0xff, (byte) 0xff, 0x00 };
+ byte[] invalidNetmask = new byte[] { (byte) 0xff, (byte) 0xfb, (byte) 0xff, 0x00 };
+ Inet4Address example1 = (Inet4Address) NetworkUtils.numericToInetAddress("192.0.2.1");
+ Inet4Address example2 = (Inet4Address) NetworkUtils.numericToInetAddress("192.0.2.43");
+
+ // A packet without any addresses is not valid.
+ checkIpAddress(null, ANY, ANY, slash24Netmask);
+
+ // ClientIP is used iff YourIP is not present.
+ checkIpAddress("192.0.2.1/24", example2, example1, slash24Netmask);
+ checkIpAddress("192.0.2.43/11", example2, ANY, slash11Netmask);
+ checkIpAddress("192.0.2.43/11", ANY, example2, slash11Netmask);
+
+ // Invalid netmasks are ignored.
+ checkIpAddress(null, example2, ANY, invalidNetmask);
+
+ // If there is no netmask, implicit netmasks are used.
+ checkIpAddress("192.0.2.43/24", ANY, example2, null);
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java b/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java
index 48d8ffb..56f1d48c 100644
--- a/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/ConnectivityServiceTest.java
@@ -20,6 +20,10 @@
import static android.net.ConnectivityManager.TYPE_MOBILE;
import static android.net.ConnectivityManager.TYPE_WIFI;
import static android.net.ConnectivityManager.getNetworkTypeName;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED;
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
+import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.eq;
import static org.mockito.Matchers.isA;
@@ -30,21 +34,36 @@
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
+import android.content.BroadcastReceiver;
import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.ConnectivityManager;
+import android.net.ConnectivityManager.NetworkCallback;
import android.net.INetworkPolicyManager;
import android.net.INetworkStatsService;
import android.net.LinkProperties;
+import android.net.Network;
+import android.net.NetworkAgent;
+import android.net.NetworkCapabilities;
import android.net.NetworkConfig;
import android.net.NetworkInfo;
import android.net.NetworkInfo.DetailedState;
+import android.net.NetworkMisc;
+import android.net.NetworkRequest;
import android.net.RouteInfo;
+import android.os.ConditionVariable;
import android.os.Handler;
+import android.os.Looper;
import android.os.INetworkManagementService;
import android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.LargeTest;
import android.util.Log;
import android.util.LogPrinter;
+import com.android.server.connectivity.NetworkMonitor;
+
import org.mockito.ArgumentCaptor;
import java.net.InetAddress;
@@ -52,8 +71,10 @@
/**
* Tests for {@link ConnectivityService}.
+ *
+ * Build, install and run with:
+ * runtest frameworks-services -c com.android.server.ConnectivityServiceTest
*/
-@LargeTest
public class ConnectivityServiceTest extends AndroidTestCase {
private static final String TAG = "ConnectivityServiceTest";
@@ -75,73 +96,336 @@
private INetworkManagementService mNetManager;
private INetworkStatsService mStatsService;
private INetworkPolicyManager mPolicyService;
-// private ConnectivityService.NetworkFactory mNetFactory;
private BroadcastInterceptingContext mServiceContext;
private ConnectivityService mService;
+ private ConnectivityManager mCm;
+ private MockNetworkAgent mWiFiNetworkAgent;
+ private MockNetworkAgent mCellNetworkAgent;
-// TODO: rework with network factory
-// private MockNetwork mMobile;
-// private MockNetwork mWifi;
-//
-// private Handler mTrackerHandler;
-//
-// private static class MockNetwork {
-// public NetworkStateTracker tracker;
-// public NetworkInfo info;
-// public LinkProperties link;
-//
-// public MockNetwork(int type) {
-// tracker = mock(NetworkStateTracker.class);
-// info = new NetworkInfo(type, -1, getNetworkTypeName(type), null);
-// link = new LinkProperties();
-// }
-//
-// public void doReturnDefaults() {
-// // TODO: eventually CS should make defensive copies
-// doReturn(new NetworkInfo(info)).when(tracker).getNetworkInfo();
-// doReturn(new LinkProperties(link)).when(tracker).getLinkProperties();
-//
-// // fallback to default TCP buffers
-// doReturn("").when(tracker).getTcpBufferSizesPropName();
-// }
-// }
-//
-// @Override
-// public void setUp() throws Exception {
-// super.setUp();
-//
-// mServiceContext = new BroadcastInterceptingContext(getContext());
-//
-// mNetManager = mock(INetworkManagementService.class);
-// mStatsService = mock(INetworkStatsService.class);
-// mPolicyService = mock(INetworkPolicyManager.class);
-// mNetFactory = mock(ConnectivityService.NetworkFactory.class);
-//
-// mMobile = new MockNetwork(TYPE_MOBILE);
-// mWifi = new MockNetwork(TYPE_WIFI);
-//
-// // omit most network trackers
-// doThrow(new IllegalArgumentException("Not supported in test environment"))
-// .when(mNetFactory).createTracker(anyInt(), isA(NetworkConfig.class));
-//
-// doReturn(mMobile.tracker)
-// .when(mNetFactory).createTracker(eq(TYPE_MOBILE), isA(NetworkConfig.class));
-// doReturn(mWifi.tracker)
-// .when(mNetFactory).createTracker(eq(TYPE_WIFI), isA(NetworkConfig.class));
-//
-// final ArgumentCaptor<Handler> trackerHandler = ArgumentCaptor.forClass(Handler.class);
-// doNothing().when(mMobile.tracker)
-// .startMonitoring(isA(Context.class), trackerHandler.capture());
-//
-// mService = new ConnectivityService(
-// mServiceContext, mNetManager, mStatsService, mPolicyService);
-// mService.systemReady();
-//
-// mTrackerHandler = trackerHandler.getValue();
-// mTrackerHandler.getLooper().setMessageLogging(new LogPrinter(Log.INFO, TAG));
-// }
-//
+ private class MockContext extends BroadcastInterceptingContext {
+ MockContext(Context base) {
+ super(base);
+ }
+
+ @Override
+ public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
+ // PendingIntents sent by the AlarmManager are not intercepted by
+ // BroadcastInterceptingContext so we must really register the receiver.
+ // This shouldn't effect the real NetworkMonitors as the action contains a random token.
+ if (filter.getAction(0).startsWith("android.net.netmon.lingerExpired")) {
+ return getBaseContext().registerReceiver(receiver, filter);
+ } else {
+ return super.registerReceiver(receiver, filter);
+ }
+ }
+
+ @Override
+ public Object getSystemService (String name) {
+ if (name == Context.CONNECTIVITY_SERVICE) return mCm;
+ return super.getSystemService(name);
+ }
+ }
+
+ private class MockNetworkAgent {
+ private final NetworkInfo mNetworkInfo;
+ private final NetworkCapabilities mNetworkCapabilities;
+ private final Thread mThread;
+ private NetworkAgent mNetworkAgent;
+
+ MockNetworkAgent(int transport) {
+ final int type = transportToLegacyType(transport);
+ final String typeName = ConnectivityManager.getNetworkTypeName(type);
+ mNetworkInfo = new NetworkInfo(type, 0, typeName, "Mock");
+ mNetworkCapabilities = new NetworkCapabilities();
+ mNetworkCapabilities.addTransportType(transport);
+ final int score;
+ switch (transport) {
+ case TRANSPORT_WIFI:
+ score = 60;
+ break;
+ case TRANSPORT_CELLULAR:
+ score = 50;
+ break;
+ default:
+ throw new UnsupportedOperationException("unimplemented network type");
+ }
+ final ConditionVariable initComplete = new ConditionVariable();
+ mThread = new Thread() {
+ public void run() {
+ Looper.prepare();
+ mNetworkAgent = new NetworkAgent(Looper.myLooper(), mServiceContext,
+ "Mock" + typeName, mNetworkInfo, mNetworkCapabilities,
+ new LinkProperties(), score, new NetworkMisc()) {
+ public void unwanted() {}
+ };
+ initComplete.open();
+ Looper.loop();
+ }
+ };
+ mThread.start();
+ initComplete.block();
+ }
+
+ /**
+ * Transition this NetworkAgent to CONNECTED state.
+ * @param validated Indicate if network should pretend to be validated.
+ */
+ public void connect(boolean validated) {
+ assertEquals(mNetworkInfo.getDetailedState(), DetailedState.IDLE);
+ assertFalse(mNetworkCapabilities.hasCapability(NET_CAPABILITY_INTERNET));
+
+ // To pretend network is validated, we transition it to the CONNECTED state without
+ // NET_CAPABILITY_INTERNET so NetworkMonitor doesn't bother trying to validate and
+ // just rubber stamps it as validated. Afterwards we add NET_CAPABILITY_INTERNET so
+ // the network can satisfy the default request.
+ NetworkCallback callback = null;
+ final ConditionVariable validatedCv = new ConditionVariable();
+ if (validated) {
+ // If we connect a network without INTERNET capability, it'll get reaped.
+ // Prevent the reaping by adding a NetworkRequest.
+ NetworkRequest request = new NetworkRequest.Builder()
+ .addTransportType(mNetworkCapabilities.getTransportTypes()[0])
+ .build();
+ callback = new NetworkCallback() {
+ public void onCapabilitiesChanged(Network network,
+ NetworkCapabilities networkCapabilities) {
+ if (networkCapabilities.hasCapability(NET_CAPABILITY_VALIDATED)) {
+ validatedCv.open();
+ }
+ }
+ };
+ mCm.requestNetwork(request, callback);
+ } else {
+ mNetworkCapabilities.addCapability(NET_CAPABILITY_INTERNET);
+ mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities);
+ }
+
+ mNetworkInfo.setDetailedState(DetailedState.CONNECTED, null, null);
+ mNetworkAgent.sendNetworkInfo(mNetworkInfo);
+
+ if (validated) {
+ // Wait for network to validate.
+ validatedCv.block();
+ mNetworkCapabilities.addCapability(NET_CAPABILITY_INTERNET);
+ mNetworkAgent.sendNetworkCapabilities(mNetworkCapabilities);
+ }
+
+ if (callback != null) mCm.unregisterNetworkCallback(callback);
+ }
+
+ public void disconnect() {
+ mNetworkInfo.setDetailedState(DetailedState.DISCONNECTED, null, null);
+ mNetworkAgent.sendNetworkInfo(mNetworkInfo);
+ }
+
+ public Network getNetwork() {
+ return new Network(mNetworkAgent.netId);
+ }
+ }
+
+ private class WrappedConnectivityService extends ConnectivityService {
+ public WrappedConnectivityService(Context context, INetworkManagementService netManager,
+ INetworkStatsService statsService, INetworkPolicyManager policyManager) {
+ super(context, netManager, statsService, policyManager);
+ }
+
+ @Override
+ protected int getDefaultTcpRwnd() {
+ // Prevent wrapped ConnectivityService from trying to write to SystemProperties.
+ return 0;
+ }
+ }
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+
+ mServiceContext = new MockContext(getContext());
+
+ mNetManager = mock(INetworkManagementService.class);
+ mStatsService = mock(INetworkStatsService.class);
+ mPolicyService = mock(INetworkPolicyManager.class);
+
+ mService = new WrappedConnectivityService(
+ mServiceContext, mNetManager, mStatsService, mPolicyService);
+ mService.systemReady();
+ mCm = new ConnectivityManager(mService);
+ }
+
+ private int transportToLegacyType(int transport) {
+ switch (transport) {
+ case TRANSPORT_WIFI:
+ return TYPE_WIFI;
+ case TRANSPORT_CELLULAR:
+ return TYPE_MOBILE;
+ default:
+ throw new IllegalStateException("Unknown transport" + transport);
+ }
+ }
+
+ private void verifyActiveNetwork(int transport) {
+ // Test getActiveNetworkInfo()
+ assertNotNull(mCm.getActiveNetworkInfo());
+ assertEquals(transportToLegacyType(transport), mCm.getActiveNetworkInfo().getType());
+ // Test getActiveNetwork()
+ assertNotNull(mCm.getActiveNetwork());
+ switch (transport) {
+ case TRANSPORT_WIFI:
+ assertEquals(mCm.getActiveNetwork(), mWiFiNetworkAgent.getNetwork());
+ break;
+ case TRANSPORT_CELLULAR:
+ assertEquals(mCm.getActiveNetwork(), mCellNetworkAgent.getNetwork());
+ break;
+ default:
+ throw new IllegalStateException("Unknown transport" + transport);
+ }
+ // Test getNetworkInfo(Network)
+ assertNotNull(mCm.getNetworkInfo(mCm.getActiveNetwork()));
+ assertEquals(transportToLegacyType(transport), mCm.getNetworkInfo(mCm.getActiveNetwork()).getType());
+ // Test getNetworkCapabilities(Network)
+ assertNotNull(mCm.getNetworkCapabilities(mCm.getActiveNetwork()));
+ assertTrue(mCm.getNetworkCapabilities(mCm.getActiveNetwork()).hasTransport(transport));
+ }
+
+ private void verifyNoNetwork() {
+ // Test getActiveNetworkInfo()
+ assertNull(mCm.getActiveNetworkInfo());
+ // Test getActiveNetwork()
+ assertNull(mCm.getActiveNetwork());
+ // Test getAllNetworks()
+ assertEquals(0, mCm.getAllNetworks().length);
+ }
+
+ /**
+ * Return a ConditionVariable that opens when {@code count} numbers of CONNECTIVITY_ACTION
+ * broadcasts are received.
+ */
+ private ConditionVariable waitForConnectivityBroadcasts(final int count) {
+ final ConditionVariable cv = new ConditionVariable();
+ mServiceContext.registerReceiver(new BroadcastReceiver() {
+ private int remaining = count;
+ public void onReceive(Context context, Intent intent) {
+ if (--remaining == 0) {
+ cv.open();
+ mServiceContext.unregisterReceiver(this);
+ }
+ }
+ }, new IntentFilter(CONNECTIVITY_ACTION));
+ return cv;
+ }
+
+ @LargeTest
+ public void testLingering() throws Exception {
+ // Decrease linger timeout to the minimum allowed by AlarmManagerService.
+ NetworkMonitor.SetDefaultLingerTime(5000);
+ verifyNoNetwork();
+ mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+ mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ assertNull(mCm.getActiveNetworkInfo());
+ assertNull(mCm.getActiveNetwork());
+ // Test bringing up validated cellular.
+ ConditionVariable cv = waitForConnectivityBroadcasts(1);
+ mCellNetworkAgent.connect(true);
+ cv.block();
+ verifyActiveNetwork(TRANSPORT_CELLULAR);
+ assertEquals(2, mCm.getAllNetworks().length);
+ assertTrue(mCm.getAllNetworks()[0].equals(mCm.getActiveNetwork()) ||
+ mCm.getAllNetworks()[1].equals(mCm.getActiveNetwork()));
+ assertTrue(mCm.getAllNetworks()[0].equals(mWiFiNetworkAgent.getNetwork()) ||
+ mCm.getAllNetworks()[1].equals(mWiFiNetworkAgent.getNetwork()));
+ // Test bringing up validated WiFi.
+ cv = waitForConnectivityBroadcasts(2);
+ mWiFiNetworkAgent.connect(true);
+ cv.block();
+ verifyActiveNetwork(TRANSPORT_WIFI);
+ assertEquals(2, mCm.getAllNetworks().length);
+ assertTrue(mCm.getAllNetworks()[0].equals(mCm.getActiveNetwork()) ||
+ mCm.getAllNetworks()[1].equals(mCm.getActiveNetwork()));
+ assertTrue(mCm.getAllNetworks()[0].equals(mCellNetworkAgent.getNetwork()) ||
+ mCm.getAllNetworks()[1].equals(mCellNetworkAgent.getNetwork()));
+ // Test cellular linger timeout.
+ try {
+ Thread.sleep(6000);
+ } catch (Exception e) {
+ }
+ verifyActiveNetwork(TRANSPORT_WIFI);
+ assertEquals(1, mCm.getAllNetworks().length);
+ assertEquals(mCm.getAllNetworks()[0], mCm.getActiveNetwork());
+ // Test WiFi disconnect.
+ cv = waitForConnectivityBroadcasts(1);
+ mWiFiNetworkAgent.disconnect();
+ cv.block();
+ verifyNoNetwork();
+ }
+
+ @LargeTest
+ public void testValidatedCellularOutscoresUnvalidatedWiFi() throws Exception {
+ // Test bringing up unvalidated WiFi
+ mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ ConditionVariable cv = waitForConnectivityBroadcasts(1);
+ mWiFiNetworkAgent.connect(false);
+ cv.block();
+ verifyActiveNetwork(TRANSPORT_WIFI);
+ // Test bringing up unvalidated cellular
+ mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+ mCellNetworkAgent.connect(false);
+ try {
+ Thread.sleep(1000);
+ } catch (Exception e) {
+ }
+ verifyActiveNetwork(TRANSPORT_WIFI);
+ // Test cellular disconnect.
+ mCellNetworkAgent.disconnect();
+ try {
+ Thread.sleep(1000);
+ } catch (Exception e) {
+ }
+ verifyActiveNetwork(TRANSPORT_WIFI);
+ // Test bringing up validated cellular
+ mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+ cv = waitForConnectivityBroadcasts(2);
+ mCellNetworkAgent.connect(true);
+ cv.block();
+ verifyActiveNetwork(TRANSPORT_CELLULAR);
+ // Test cellular disconnect.
+ cv = waitForConnectivityBroadcasts(2);
+ mCellNetworkAgent.disconnect();
+ cv.block();
+ verifyActiveNetwork(TRANSPORT_WIFI);
+ // Test WiFi disconnect.
+ cv = waitForConnectivityBroadcasts(1);
+ mWiFiNetworkAgent.disconnect();
+ cv.block();
+ verifyNoNetwork();
+ }
+
+ @LargeTest
+ public void testUnvalidatedWifiOutscoresUnvalidatedCellular() throws Exception {
+ // Test bringing up unvalidated cellular.
+ mCellNetworkAgent = new MockNetworkAgent(TRANSPORT_CELLULAR);
+ ConditionVariable cv = waitForConnectivityBroadcasts(1);
+ mCellNetworkAgent.connect(false);
+ cv.block();
+ verifyActiveNetwork(TRANSPORT_CELLULAR);
+ // Test bringing up unvalidated WiFi.
+ mWiFiNetworkAgent = new MockNetworkAgent(TRANSPORT_WIFI);
+ cv = waitForConnectivityBroadcasts(2);
+ mWiFiNetworkAgent.connect(false);
+ cv.block();
+ verifyActiveNetwork(TRANSPORT_WIFI);
+ // Test WiFi disconnect.
+ cv = waitForConnectivityBroadcasts(2);
+ mWiFiNetworkAgent.disconnect();
+ cv.block();
+ verifyActiveNetwork(TRANSPORT_CELLULAR);
+ // Test cellular disconnect.
+ cv = waitForConnectivityBroadcasts(1);
+ mCellNetworkAgent.disconnect();
+ cv.block();
+ verifyNoNetwork();
+ }
+
// @Override
// public void tearDown() throws Exception {
// super.tearDown();
@@ -157,9 +441,9 @@
// mMobile.link.addRoute(MOBILE_ROUTE_V6);
// mMobile.doReturnDefaults();
//
-// nextConnBroadcast = mServiceContext.nextBroadcastIntent(CONNECTIVITY_ACTION);
+// cv = waitForConnectivityBroadcasts(1);
// mTrackerHandler.obtainMessage(EVENT_STATE_CHANGED, mMobile.info).sendToTarget();
-// nextConnBroadcast.get();
+// cv.block();
//
// // verify that both routes were added
// int mobileNetId = mMobile.tracker.getNetwork().netId;
@@ -177,9 +461,9 @@
// mMobile.link.addRoute(MOBILE_ROUTE_V6);
// mMobile.doReturnDefaults();
//
-// nextConnBroadcast = mServiceContext.nextBroadcastIntent(CONNECTIVITY_ACTION);
+// cv = waitForConnectivityBroadcasts(1);
// mTrackerHandler.obtainMessage(EVENT_STATE_CHANGED, mMobile.info).sendToTarget();
-// nextConnBroadcast.get();
+// cv.block();
//
// reset(mNetManager);
//
@@ -193,9 +477,9 @@
// // expect that mobile will be torn down
// doReturn(true).when(mMobile.tracker).teardown();
//
-// nextConnBroadcast = mServiceContext.nextBroadcastIntent(CONNECTIVITY_ACTION);
+// cv = waitForConnectivityBroadcasts(1);
// mTrackerHandler.obtainMessage(EVENT_STATE_CHANGED, mWifi.info).sendToTarget();
-// nextConnBroadcast.get();
+// cv.block();
//
// // verify that wifi routes added, and teardown requested
// int wifiNetId = mWifi.tracker.getNetwork().netId;
@@ -212,9 +496,9 @@
// mMobile.link.clear();
// mMobile.doReturnDefaults();
//
-// nextConnBroadcast = mServiceContext.nextBroadcastIntent(CONNECTIVITY_ACTION);
+// cv = waitForConnectivityBroadcasts(1);
// mTrackerHandler.obtainMessage(EVENT_STATE_CHANGED, mMobile.info).sendToTarget();
-// nextConnBroadcast.get();
+// cv.block();
//
// verify(mNetManager).removeRoute(eq(mobileNetId), eq(MOBILE_ROUTE_V4));
// verify(mNetManager).removeRoute(eq(mobileNetId), eq(MOBILE_ROUTE_V6));
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
index 6de887b..d8569bc 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
@@ -201,7 +201,7 @@
Slog.w(TAG, "finish does not match active session");
return;
}
- mActiveSession.cancel();
+ mActiveSession.cancelLocked();
mActiveSession = null;
}
@@ -251,7 +251,7 @@
// If there is an active session, cancel it to allow it to clean up its window and other
// state.
if (mActiveSession != null) {
- mActiveSession.cancel();
+ mActiveSession.cancelLocked();
mActiveSession = null;
}
try {
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
index b4629f2..0b430ca0 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
@@ -19,9 +19,9 @@
import android.app.ActivityManager;
import android.app.ActivityManagerNative;
import android.app.AppOpsManager;
-import android.app.AssistContent;
-import android.app.AssistStructure;
import android.app.IActivityManager;
+import android.app.assist.AssistContent;
+import android.app.assist.AssistStructure;
import android.content.ClipData;
import android.content.ComponentName;
import android.content.ContentProvider;
@@ -376,6 +376,40 @@
return false;
}
+ public void cancelLocked() {
+ hideLocked();
+ mCanceled = true;
+ if (mBound) {
+ if (mSession != null) {
+ try {
+ mSession.destroy();
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Voice interation session already dead");
+ }
+ }
+ if (mSession != null) {
+ try {
+ mAm.finishVoiceTask(mSession);
+ } catch (RemoteException e) {
+ }
+ }
+ mContext.unbindService(this);
+ try {
+ mIWindowManager.removeWindowToken(mToken);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed removing window token", e);
+ }
+ mBound = false;
+ mService = null;
+ mSession = null;
+ mInteractor = null;
+ }
+ if (mFullyBound) {
+ mContext.unbindService(mFullConnection);
+ mFullyBound = false;
+ }
+ }
+
public boolean deliverNewSessionLocked(IVoiceInteractionSession session,
IVoiceInteractor interactor) {
mSession = session;
@@ -432,39 +466,6 @@
mService = null;
}
- public void cancel() {
- mCanceled = true;
- if (mBound) {
- if (mSession != null) {
- try {
- mSession.destroy();
- } catch (RemoteException e) {
- Slog.w(TAG, "Voice interation session already dead");
- }
- }
- if (mSession != null) {
- try {
- mAm.finishVoiceTask(mSession);
- } catch (RemoteException e) {
- }
- }
- mContext.unbindService(this);
- try {
- mIWindowManager.removeWindowToken(mToken);
- } catch (RemoteException e) {
- Slog.w(TAG, "Failed removing window token", e);
- }
- mBound = false;
- mService = null;
- mSession = null;
- mInteractor = null;
- }
- if (mFullyBound) {
- mContext.unbindService(mFullConnection);
- mFullyBound = false;
- }
- }
-
private boolean isStructureEnabled() {
return Settings.Secure.getIntForUser(mContext.getContentResolver(),
Settings.Secure.ASSIST_STRUCTURE_ENABLED, 1, mUser) != 0;
diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index 1d751d5..dfb02bb 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -51,6 +51,10 @@
public static final String
ACTION_CARRIER_CONFIG_CHANGED = "android.telephony.action.CARRIER_CONFIG_CHANGED";
+ // Below are the keys used in carrier config bundles. To add a new variable, define the key and
+ // give it a default value in sDefaults. If you need to ship a per-network override in the
+ // system image, that can be added in packages/apps/CarrierConfig.
+
/**
* Flag indicating whether the Phone app should ignore EVENT_SIM_NETWORK_LOCKED
* events from the Sim.
diff --git a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionSession.java b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionSession.java
index f66a9ce..dae1ac3 100644
--- a/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionSession.java
+++ b/tests/VoiceInteraction/src/com/android/test/voiceinteraction/MainInteractionSession.java
@@ -17,9 +17,9 @@
package com.android.test.voiceinteraction;
import android.app.ActivityManager;
+import android.app.VoiceInteractor;
import android.app.AssistContent;
import android.app.AssistStructure;
-import android.app.VoiceInteractor;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
@@ -71,7 +71,7 @@
public void onCreate(Bundle args, int startFlags) {
super.onCreate(args, startFlags);
ActivityManager am = getContext().getSystemService(ActivityManager.class);
- am.setWatchHeapLimit(40*1024*1024);
+ am.setWatchHeapLimit(40 * 1024 * 1024);
}
@Override
@@ -118,6 +118,30 @@
return mContentView;
}
+ public void onHandleAssist(Bundle assistBundle) {
+ if (assistBundle != null) {
+ Bundle assistContext = assistBundle.getBundle(Intent.EXTRA_ASSIST_CONTEXT);
+ if (assistContext != null) {
+ mAssistStructure = AssistStructure.getAssistStructure(assistContext);
+ if (mAssistStructure != null) {
+ if (mAssistVisualizer != null) {
+ mAssistVisualizer.setAssistStructure(mAssistStructure);
+ }
+ }
+ AssistContent content = AssistContent.getAssistContent(assistContext);
+ if (content != null) {
+ Log.i(TAG, "Assist intent: " + content.getIntent());
+ Log.i(TAG, "Assist clipdata: " + content.getClipData());
+ }
+ return;
+ }
+ }
+ if (mAssistVisualizer != null) {
+ mAssistVisualizer.clearAssistData();
+ }
+ }
+
+ /*
@Override
public void onHandleAssist(Bundle data, AssistStructure structure, AssistContent content) {
mAssistStructure = structure;
@@ -131,6 +155,7 @@
Log.i(TAG, "Assist clipdata: " + content.getClipData());
}
}
+ */
@Override
public void onHandleScreenshot(Bitmap screenshot) {
diff --git a/wifi/java/android/net/wifi/WifiScanner.java b/wifi/java/android/net/wifi/WifiScanner.java
index 5f430f0..a3dc077 100644
--- a/wifi/java/android/net/wifi/WifiScanner.java
+++ b/wifi/java/android/net/wifi/WifiScanner.java
@@ -1007,7 +1007,7 @@
((ParcelableScanResults) msg.obj).getResults());
return;
case CMD_SINGLE_SCAN_COMPLETED:
- Log.d(TAG, "removing listener for single scan");
+ if (DBG) Log.d(TAG, "removing listener for single scan");
removeListener(msg.arg2);
break;
default: