summaryrefslogtreecommitdiffstats
path: root/core/java/android
diff options
context:
space:
mode:
Diffstat (limited to 'core/java/android')
-rw-r--r--core/java/android/accounts/AccountManager.java66
-rw-r--r--core/java/android/accounts/ChooseTypeAndAccountActivity.java16
-rw-r--r--core/java/android/app/ActivityThread.java14
-rw-r--r--core/java/android/app/AlarmManager.java26
-rw-r--r--core/java/android/app/ApplicationPackageManager.java9
-rw-r--r--core/java/android/app/ISearchManager.aidl4
-rw-r--r--core/java/android/app/SearchManager.java49
-rw-r--r--core/java/android/app/assist/AssistStructure.java341
-rw-r--r--core/java/android/bluetooth/BluetoothSocket.java2
-rw-r--r--core/java/android/bluetooth/IBluetoothGatt.aidl2
-rw-r--r--core/java/android/bluetooth/le/BluetoothLeScanner.java3
-rw-r--r--core/java/android/content/pm/IPackageManager.aidl5
-rw-r--r--core/java/android/content/pm/IntentFilterVerificationInfo.java28
-rw-r--r--core/java/android/content/pm/PackageManager.java17
-rw-r--r--core/java/android/content/pm/PackageParser.java1
-rw-r--r--core/java/android/content/pm/PackageUserState.java2
-rw-r--r--core/java/android/content/res/ColorStateList.java2
-rw-r--r--core/java/android/content/res/Resources.java1
-rw-r--r--core/java/android/hardware/SystemSensorManager.java32
-rw-r--r--core/java/android/hardware/camera2/CameraCaptureSession.java11
-rw-r--r--core/java/android/hardware/camera2/CameraCharacteristics.java2
-rw-r--r--core/java/android/hardware/camera2/CameraConstrainedHighSpeedCaptureSession.java131
-rw-r--r--core/java/android/hardware/camera2/CameraDevice.java65
-rw-r--r--core/java/android/hardware/camera2/CameraMetadata.java23
-rw-r--r--core/java/android/hardware/camera2/CaptureRequest.java25
-rw-r--r--core/java/android/hardware/camera2/impl/CameraCaptureSessionCore.java64
-rw-r--r--core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java58
-rw-r--r--core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java283
-rw-r--r--core/java/android/hardware/camera2/impl/CameraDeviceImpl.java213
-rw-r--r--core/java/android/hardware/camera2/params/StreamConfigurationMap.java15
-rw-r--r--core/java/android/hardware/camera2/utils/SurfaceUtils.java96
-rw-r--r--core/java/android/hardware/usb/IUsbManager.aidl3
-rw-r--r--core/java/android/hardware/usb/UsbManager.java130
-rw-r--r--core/java/android/inputmethodservice/ExtractEditText.java29
-rw-r--r--core/java/android/net/CaptivePortal.java108
-rw-r--r--core/java/android/net/ConnectivityManager.java122
-rw-r--r--core/java/android/net/ICaptivePortal.aidl26
-rw-r--r--core/java/android/net/IConnectivityManager.aidl4
-rw-r--r--core/java/android/net/NetworkCapabilities.java3
-rw-r--r--core/java/android/os/PooledStringReader.java4
-rw-r--r--core/java/android/os/PooledStringWriter.java4
-rw-r--r--core/java/android/os/storage/IMountService.java41
-rw-r--r--core/java/android/os/storage/MountServiceInternal.java82
-rw-r--r--core/java/android/os/storage/StorageManager.java16
-rw-r--r--core/java/android/os/storage/VolumeInfo.java6
-rw-r--r--core/java/android/provider/Settings.java17
-rw-r--r--core/java/android/service/voice/VoiceInteractionSession.java72
-rw-r--r--core/java/android/text/BoringLayout.java10
-rw-r--r--core/java/android/text/Html.java7
-rw-r--r--core/java/android/text/Layout.java2
-rw-r--r--core/java/android/text/SpannableStringInternal.java9
-rw-r--r--core/java/android/text/StaticLayout.java3
-rw-r--r--core/java/android/util/LocalLog.java6
-rw-r--r--core/java/android/view/WindowManagerPolicy.java8
-rw-r--r--core/java/android/widget/AppSecurityPermissions.java53
-rw-r--r--core/java/android/widget/Editor.java66
-rw-r--r--core/java/android/widget/ImageView.java29
-rw-r--r--core/java/android/widget/TextView.java29
58 files changed, 1716 insertions, 779 deletions
diff --git a/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java
index aa7692b..dd3d3a8 100644
--- a/core/java/android/accounts/AccountManager.java
+++ b/core/java/android/accounts/AccountManager.java
@@ -43,6 +43,7 @@ import com.google.android.collect.Maps;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.CancellationException;
@@ -2259,6 +2260,9 @@ public class AccountManager {
}
/**
+ * Deprecated in favor of {@link #newChooseAccountIntent(Account, List, String[], String,
+ * String, String[], Bundle)}.
+ *
* Returns an intent to an {@link Activity} that prompts the user to choose from a list of
* accounts.
* The caller will then typically start the activity by calling
@@ -2273,14 +2277,13 @@ public class AccountManager {
* null, null, null);</pre>
* @param selectedAccount if specified, indicates that the {@link Account} is the currently
* selected one, according to the caller's definition of selected.
- * @param allowableAccounts an optional {@link ArrayList} of accounts that are allowed to be
+ * @param allowableAccounts an optional {@link List} of accounts that are allowed to be
* shown. If not specified then this field will not limit the displayed accounts.
* @param allowableAccountTypes an optional string array of account types. These are used
* both to filter the shown accounts and to filter the list of account types that are shown
* when adding an account. If not specified then this field will not limit the displayed
* account types when adding an account.
- * @param alwaysPromptForAccount if set the account chooser screen is always shown, otherwise
- * it is only shown when there is more than one account from which to choose
+ * @param alwaysPromptForAccount boolean that is ignored.
* @param descriptionOverrideText if non-null this string is used as the description in the
* accounts chooser screen rather than the default
* @param addAccountAuthTokenType this string is passed as the {@link #addAccount}
@@ -2291,7 +2294,9 @@ public class AccountManager {
* parameter
* @return an {@link Intent} that can be used to launch the ChooseAccount activity flow.
*/
- static public Intent newChooseAccountIntent(Account selectedAccount,
+ @Deprecated
+ static public Intent newChooseAccountIntent(
+ Account selectedAccount,
ArrayList<Account> allowableAccounts,
String[] allowableAccountTypes,
boolean alwaysPromptForAccount,
@@ -2299,20 +2304,67 @@ public class AccountManager {
String addAccountAuthTokenType,
String[] addAccountRequiredFeatures,
Bundle addAccountOptions) {
+ return newChooseAccountIntent(
+ selectedAccount,
+ allowableAccounts,
+ allowableAccountTypes,
+ descriptionOverrideText,
+ addAccountAuthTokenType,
+ addAccountRequiredFeatures,
+ addAccountOptions);
+ }
+
+ /**
+ * Returns an intent to an {@link Activity} that prompts the user to choose from a list of
+ * accounts.
+ * The caller will then typically start the activity by calling
+ * <code>startActivityForResult(intent, ...);</code>.
+ * <p>
+ * On success the activity returns a Bundle with the account name and type specified using
+ * keys {@link #KEY_ACCOUNT_NAME} and {@link #KEY_ACCOUNT_TYPE}.
+ * <p>
+ * The most common case is to call this with one account type, e.g.:
+ * <p>
+ * <pre> newChooseAccountIntent(null, null, new String[]{"com.google"}, null, null, null,
+ * null);</pre>
+ * @param selectedAccount if specified, indicates that the {@link Account} is the currently
+ * selected one, according to the caller's definition of selected.
+ * @param allowableAccounts an optional {@link List} of accounts that are allowed to be
+ * shown. If not specified then this field will not limit the displayed accounts.
+ * @param allowableAccountTypes an optional string array of account types. These are used
+ * both to filter the shown accounts and to filter the list of account types that are shown
+ * when adding an account. If not specified then this field will not limit the displayed
+ * account types when adding an account.
+ * @param descriptionOverrideText if non-null this string is used as the description in the
+ * accounts chooser screen rather than the default
+ * @param addAccountAuthTokenType this string is passed as the {@link #addAccount}
+ * authTokenType parameter
+ * @param addAccountRequiredFeatures this string array is passed as the {@link #addAccount}
+ * requiredFeatures parameter
+ * @param addAccountOptions This {@link Bundle} is passed as the {@link #addAccount} options
+ * parameter
+ * @return an {@link Intent} that can be used to launch the ChooseAccount activity flow.
+ */
+ static public Intent newChooseAccountIntent(
+ Account selectedAccount,
+ List<Account> allowableAccounts,
+ String[] allowableAccountTypes,
+ String descriptionOverrideText,
+ String addAccountAuthTokenType,
+ String[] addAccountRequiredFeatures,
+ Bundle addAccountOptions) {
Intent intent = new Intent();
ComponentName componentName = ComponentName.unflattenFromString(
Resources.getSystem().getString(R.string.config_chooseTypeAndAccountActivity));
intent.setClassName(componentName.getPackageName(),
componentName.getClassName());
intent.putExtra(ChooseTypeAndAccountActivity.EXTRA_ALLOWABLE_ACCOUNTS_ARRAYLIST,
- allowableAccounts);
+ allowableAccounts == null ? null : new ArrayList<Account>(allowableAccounts));
intent.putExtra(ChooseTypeAndAccountActivity.EXTRA_ALLOWABLE_ACCOUNT_TYPES_STRING_ARRAY,
allowableAccountTypes);
intent.putExtra(ChooseTypeAndAccountActivity.EXTRA_ADD_ACCOUNT_OPTIONS_BUNDLE,
addAccountOptions);
intent.putExtra(ChooseTypeAndAccountActivity.EXTRA_SELECTED_ACCOUNT, selectedAccount);
- intent.putExtra(ChooseTypeAndAccountActivity.EXTRA_ALWAYS_PROMPT_FOR_ACCOUNT,
- alwaysPromptForAccount);
intent.putExtra(ChooseTypeAndAccountActivity.EXTRA_DESCRIPTION_TEXT_OVERRIDE,
descriptionOverrideText);
intent.putExtra(ChooseTypeAndAccountActivity.EXTRA_ADD_ACCOUNT_AUTH_TOKEN_TYPE_STRING,
diff --git a/core/java/android/accounts/ChooseTypeAndAccountActivity.java b/core/java/android/accounts/ChooseTypeAndAccountActivity.java
index c06b462..133df2b 100644
--- a/core/java/android/accounts/ChooseTypeAndAccountActivity.java
+++ b/core/java/android/accounts/ChooseTypeAndAccountActivity.java
@@ -88,9 +88,10 @@ public class ChooseTypeAndAccountActivity extends Activity
public static final String EXTRA_SELECTED_ACCOUNT = "selectedAccount";
/**
- * If true then display the account selection list even if there is just
- * one account to choose from. boolean.
+ * Deprecated. Providing this extra to {@link ChooseTypeAndAccountActivity}
+ * will have no effect.
*/
+ @Deprecated
public static final String EXTRA_ALWAYS_PROMPT_FOR_ACCOUNT =
"alwaysPromptForAccount";
@@ -117,7 +118,6 @@ public class ChooseTypeAndAccountActivity extends Activity
private Set<String> mSetOfRelevantAccountTypes;
private String mSelectedAccountName = null;
private boolean mSelectedAddNewAccount = false;
- private boolean mAlwaysPromptForAccount = false;
private String mDescriptionOverride;
private ArrayList<Account> mAccounts;
@@ -188,7 +188,6 @@ public class ChooseTypeAndAccountActivity extends Activity
mSetOfAllowableAccounts = getAllowableAccountSet(intent);
mSetOfRelevantAccountTypes = getReleventAccountTypes(intent);
- mAlwaysPromptForAccount = intent.getBooleanExtra(EXTRA_ALWAYS_PROMPT_FOR_ACCOUNT, false);
mDescriptionOverride = intent.getStringExtra(EXTRA_DESCRIPTION_TEXT_OVERRIDE);
mAccounts = getAcceptableAccountChoices(AccountManager.get(this));
@@ -218,15 +217,6 @@ public class ChooseTypeAndAccountActivity extends Activity
} else {
startChooseAccountTypeActivity();
}
- return;
- }
-
- // if there is only one allowable account return it
- if (!mAlwaysPromptForAccount && mAccounts.size() == 1) {
- Account account = mAccounts.get(0);
- super.onCreate(savedInstanceState);
- setResultAndFinish(account.name, account.type);
- return;
}
}
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 828dc0a..2b4d03b 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -180,15 +180,14 @@ public final class ActivityThread {
final ApplicationThread mAppThread = new ApplicationThread();
final Looper mLooper = Looper.myLooper();
final H mH = new H();
- final ArrayMap<IBinder, ActivityClientRecord> mActivities
- = new ArrayMap<IBinder, ActivityClientRecord>();
+ final ArrayMap<IBinder, ActivityClientRecord> mActivities = new ArrayMap<>();
// List of new activities (via ActivityRecord.nextIdle) that should
// be reported when next we idle.
ActivityClientRecord mNewActivities = null;
// Number of activities that are currently visible on-screen.
int mNumVisibleActivities = 0;
- final ArrayMap<IBinder, Service> mServices
- = new ArrayMap<IBinder, Service>();
+ WeakReference<AssistStructure> mLastAssistStructure;
+ final ArrayMap<IBinder, Service> mServices = new ArrayMap<>();
AppBindData mBoundApplication;
Profiler mProfiler;
int mCurDefaultDisplayDpi;
@@ -2568,6 +2567,12 @@ public final class ActivityThread {
}
public void handleRequestAssistContextExtras(RequestAssistContextExtras cmd) {
+ if (mLastAssistStructure != null) {
+ AssistStructure structure = mLastAssistStructure.get();
+ if (structure != null) {
+ structure.clearSendChannel();
+ }
+ }
Bundle data = new Bundle();
AssistStructure structure = null;
AssistContent content = new AssistContent();
@@ -2597,6 +2602,7 @@ public final class ActivityThread {
if (structure == null) {
structure = new AssistStructure();
}
+ mLastAssistStructure = new WeakReference<>(structure);
IActivityManager mgr = ActivityManagerNative.getDefault();
try {
mgr.reportAssistContextExtras(cmd.requestToken, data, structure, content, referrer);
diff --git a/core/java/android/app/AlarmManager.java b/core/java/android/app/AlarmManager.java
index 9ea1606..dc83a01 100644
--- a/core/java/android/app/AlarmManager.java
+++ b/core/java/android/app/AlarmManager.java
@@ -570,12 +570,15 @@ public class AlarmManager {
* even when the system is in low-power idle modes. This type of alarm must <b>only</b>
* be used for situations where it is actually required that the alarm go off while in
* idle -- a reasonable example would be for a calendar notification that should make a
- * sound so the user is aware of it. These alarms can significantly impact the power use
- * of the device when idle (and thus cause significant battery blame to the app scheduling
- * them), so they should be used with care.
+ * sound so the user is aware of it. When the alarm is dispatched, the app will also be
+ * added to the system's temporary whitelist for approximately 10 seconds to allow that
+ * application to acquire further wake locks in which to complete its work.</p>
*
- * <p>To reduce abuse, there are restrictions on how frequently these alarms will go off
- * for a particular application. Under normal system operation, it will not dispatch these
+ * <p>These alarms can significantly impact the power use
+ * of the device when idle (and thus cause significant battery blame to the app scheduling
+ * them), so they should be used with care. To reduce abuse, there are restrictions on how
+ * frequently these alarms will go off for a particular application.
+ * Under normal system operation, it will not dispatch these
* alarms more than about every minute (at which point every such pending alarm is
* dispatched); when in low-power idle modes this duration may be significantly longer,
* such as 15 minutes.</p>
@@ -619,12 +622,15 @@ public class AlarmManager {
* {@link #setAndAllowWhileIdle}. This type of alarm must <b>only</b>
* be used for situations where it is actually required that the alarm go off while in
* idle -- a reasonable example would be for a calendar notification that should make a
- * sound so the user is aware of it. These alarms can significantly impact the power use
- * of the device when idle (and thus cause significant battery blame to the app scheduling
- * them), so they should be used with care.
+ * sound so the user is aware of it. When the alarm is dispatched, the app will also be
+ * added to the system's temporary whitelist for approximately 10 seconds to allow that
+ * application to acquire further wake locks in which to complete its work.</p>
*
- * <p>To reduce abuse, there are restrictions on how frequently these alarms will go off
- * for a particular application. Under normal system operation, it will not dispatch these
+ * <p>These alarms can significantly impact the power use
+ * of the device when idle (and thus cause significant battery blame to the app scheduling
+ * them), so they should be used with care. To reduce abuse, there are restrictions on how
+ * frequently these alarms will go off for a particular application.
+ * Under normal system operation, it will not dispatch these
* alarms more than about every minute (at which point every such pending alarm is
* dispatched); when in low-power idle modes this duration may be significantly longer,
* such as 15 minutes.</p>
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index cb1e7aa..1fb0b2a 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -421,6 +421,15 @@ final class ApplicationPackageManager extends PackageManager {
}
@Override
+ public boolean isPermissionRevokedByPolicy(String permName, String pkgName) {
+ try {
+ return mPM.isPermissionRevokedByPolicy(permName, pkgName, mContext.getUserId());
+ } catch (RemoteException e) {
+ throw new RuntimeException("Package manager has died", e);
+ }
+ }
+
+ @Override
public boolean addPermission(PermissionInfo info) {
try {
return mPM.addPermission(info);
diff --git a/core/java/android/app/ISearchManager.aidl b/core/java/android/app/ISearchManager.aidl
index 6094012..0d09e4a 100644
--- a/core/java/android/app/ISearchManager.aidl
+++ b/core/java/android/app/ISearchManager.aidl
@@ -30,6 +30,6 @@ interface ISearchManager {
List<ResolveInfo> getGlobalSearchActivities();
ComponentName getGlobalSearchActivity();
ComponentName getWebSearchActivity();
- ComponentName getAssistIntent(int userHandle);
- boolean launchAssistAction(String hint, int userHandle, in Bundle args);
+ void launchAssist(in Bundle args);
+ boolean launchLegacyAssist(String hint, int userHandle, in Bundle args);
}
diff --git a/core/java/android/app/SearchManager.java b/core/java/android/app/SearchManager.java
index 45799a1..9e32164 100644
--- a/core/java/android/app/SearchManager.java
+++ b/core/java/android/app/SearchManager.java
@@ -946,27 +946,9 @@ public class SearchManager
*
* @hide
*/
- public Intent getAssistIntent(Context context, boolean inclContext) {
- return getAssistIntent(context, inclContext, UserHandle.myUserId());
- }
-
- /**
- * Gets an intent for launching installed assistant activity, or null if not available.
- * @return The assist intent.
- *
- * @hide
- */
- public Intent getAssistIntent(Context context, boolean inclContext, int userHandle) {
+ public Intent getAssistIntent(boolean inclContext) {
try {
- if (mService == null) {
- return null;
- }
- ComponentName comp = mService.getAssistIntent(userHandle);
- if (comp == null) {
- return null;
- }
Intent intent = new Intent(Intent.ACTION_ASSIST);
- intent.setComponent(comp);
if (inclContext) {
IActivityManager am = ActivityManagerNative.getDefault();
Bundle extras = am.getAssistContextExtras(ActivityManager.ASSIST_CONTEXT_BASIC);
@@ -982,17 +964,38 @@ public class SearchManager
}
/**
- * Launch an assist action for the current top activity.
+ * Starts the assistant.
+ *
+ * @param args the args to pass to the assistant
+ *
+ * @hide
+ */
+ public void launchAssist(Bundle args) {
+ try {
+ if (mService == null) {
+ return;
+ }
+ mService.launchAssist(args);
+ } catch (RemoteException re) {
+ Log.e(TAG, "launchAssist() failed: " + re);
+ }
+ }
+
+ /**
+ * Starts the legacy assistant (i.e. the {@link Intent#ACTION_ASSIST}).
+ *
+ * @param args the args to pass to the assistant
+ *
* @hide
*/
- public boolean launchAssistAction(String hint, int userHandle, Bundle args) {
+ public boolean launchLegacyAssist(String hint, int userHandle, Bundle args) {
try {
if (mService == null) {
return false;
}
- return mService.launchAssistAction(hint, userHandle, args);
+ return mService.launchLegacyAssist(hint, userHandle, args);
} catch (RemoteException re) {
- Log.e(TAG, "launchAssistAction() failed: " + re);
+ Log.e(TAG, "launchAssist() failed: " + re);
return false;
}
}
diff --git a/core/java/android/app/assist/AssistStructure.java b/core/java/android/app/assist/AssistStructure.java
index 73c551f..9673c98 100644
--- a/core/java/android/app/assist/AssistStructure.java
+++ b/core/java/android/app/assist/AssistStructure.java
@@ -30,6 +30,9 @@ import java.util.ArrayList;
public class AssistStructure implements Parcelable {
static final String TAG = "AssistStructure";
+ static final boolean DEBUG_PARCEL = false;
+ static final boolean DEBUG_PARCEL_TREE = false;
+
boolean mHaveData;
ComponentName mActivityComponent;
@@ -46,12 +49,40 @@ public class AssistStructure implements Parcelable {
static final int TRANSACTION_XFER = Binder.FIRST_CALL_TRANSACTION+1;
static final String DESCRIPTOR = "android.app.AssistStructure";
- final class SendChannel extends Binder {
+ final static class SendChannel extends Binder {
+ volatile AssistStructure mAssistStructure;
+
+ SendChannel(AssistStructure as) {
+ mAssistStructure = as;
+ }
+
@Override protected boolean onTransact(int code, Parcel data, Parcel reply, int flags)
throws RemoteException {
if (code == TRANSACTION_XFER) {
+ AssistStructure as = mAssistStructure;
+ if (as == null) {
+ return true;
+ }
+
data.enforceInterface(DESCRIPTOR);
- writeContentToParcel(reply, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
+ IBinder token = data.readStrongBinder();
+ if (DEBUG_PARCEL) Log.d(TAG, "Request for data on " + as
+ + " using token " + token);
+ if (token != null) {
+ if (DEBUG_PARCEL) Log.d(TAG, "Resuming partial write of " + token);
+ if (token instanceof ParcelTransferWriter) {
+ ParcelTransferWriter xfer = (ParcelTransferWriter)token;
+ xfer.writeToParcel(as, reply);
+ return true;
+ }
+ Log.w(TAG, "Caller supplied bad token type: " + token);
+ // Don't write anything; this is the end of the data.
+ return true;
+ }
+ //long start = SystemClock.uptimeMillis();
+ ParcelTransferWriter xfer = new ParcelTransferWriter(as, reply);
+ xfer.writeToParcel(as, reply);
+ //Log.i(TAG, "Time to parcel: " + (SystemClock.uptimeMillis()-start) + "ms");
return true;
} else {
return super.onTransact(code, data, reply, flags);
@@ -59,6 +90,235 @@ public class AssistStructure implements Parcelable {
}
}
+ final static class ViewStackEntry {
+ ViewNode node;
+ int curChild;
+ int numChildren;
+ }
+
+ final static class ParcelTransferWriter extends Binder {
+ final boolean mWriteStructure;
+ int mCurWindow;
+ int mNumWindows;
+ final ArrayList<ViewStackEntry> mViewStack = new ArrayList<>();
+ ViewStackEntry mCurViewStackEntry;
+ int mCurViewStackPos;
+ int mNumWrittenWindows;
+ int mNumWrittenViews;
+ final float[] mTmpMatrix = new float[9];
+
+ ParcelTransferWriter(AssistStructure as, Parcel out) {
+ mWriteStructure = as.waitForReady();
+ ComponentName.writeToParcel(as.mActivityComponent, out);
+ mNumWindows = as.mWindowNodes.size();
+ if (mWriteStructure && mNumWindows > 0) {
+ out.writeInt(mNumWindows);
+ } else {
+ out.writeInt(0);
+ }
+ }
+
+ void writeToParcel(AssistStructure as, Parcel out) {
+ int start = out.dataPosition();
+ mNumWrittenWindows = 0;
+ mNumWrittenViews = 0;
+ boolean more = writeToParcelInner(as, out);
+ Log.i(TAG, "Flattened " + (more ? "partial" : "final") + " assist data: "
+ + (out.dataPosition() - start)
+ + " bytes, containing " + mNumWrittenWindows + " windows, "
+ + mNumWrittenViews + " views");
+ }
+
+ boolean writeToParcelInner(AssistStructure as, Parcel out) {
+ if (mNumWindows == 0) {
+ return false;
+ }
+ if (DEBUG_PARCEL) Log.d(TAG, "Creating PooledStringWriter @ " + out.dataPosition());
+ PooledStringWriter pwriter = new PooledStringWriter(out);
+ while (writeNextEntryToParcel(as, out, pwriter)) {
+ // If the parcel contains more than 100K of data, then we are getting too
+ // large for a single IPC so stop here and let the caller come back when it
+ // is ready for more.
+ if (out.dataSize() > 1024*1024) {
+ if (DEBUG_PARCEL) Log.d(TAG, "Assist data size is " + out.dataSize()
+ + " @ pos " + out.dataPosition() + "; returning partial result");
+ out.writeInt(0);
+ out.writeStrongBinder(this);
+ if (DEBUG_PARCEL) Log.d(TAG, "Finishing PooledStringWriter @ "
+ + out.dataPosition() + ", size " + pwriter.getStringCount());
+ pwriter.finish();
+ return true;
+ }
+ }
+ if (DEBUG_PARCEL) Log.d(TAG, "Finishing PooledStringWriter @ "
+ + out.dataPosition() + ", size " + pwriter.getStringCount());
+ pwriter.finish();
+ mViewStack.clear();
+ return false;
+ }
+
+ void pushViewStackEntry(ViewNode node, int pos) {
+ ViewStackEntry entry;
+ if (pos >= mViewStack.size()) {
+ entry = new ViewStackEntry();
+ mViewStack.add(entry);
+ if (DEBUG_PARCEL_TREE) Log.d(TAG, "New stack entry at " + pos + ": " + entry);
+ } else {
+ entry = mViewStack.get(pos);
+ if (DEBUG_PARCEL_TREE) Log.d(TAG, "Existing stack entry at " + pos + ": " + entry);
+ }
+ entry.node = node;
+ entry.numChildren = node.getChildCount();
+ entry.curChild = 0;
+ mCurViewStackEntry = entry;
+ }
+
+ boolean writeNextEntryToParcel(AssistStructure as, Parcel out, PooledStringWriter pwriter) {
+ // Write next view node if appropriate.
+ if (mCurViewStackEntry != null) {
+ if (mCurViewStackEntry.curChild < mCurViewStackEntry.numChildren) {
+ // Write the next child in the current view.
+ if (DEBUG_PARCEL_TREE) Log.d(TAG, "Writing child #"
+ + mCurViewStackEntry.curChild + " in " + mCurViewStackEntry.node);
+ ViewNode child = mCurViewStackEntry.node.mChildren[mCurViewStackEntry.curChild];
+ mCurViewStackEntry.curChild++;
+ if (DEBUG_PARCEL) Log.d(TAG, "write view: at " + out.dataPosition()
+ + ", windows=" + mNumWrittenWindows
+ + ", views=" + mNumWrittenViews);
+ out.writeInt(1);
+ int flags = child.writeSelfToParcel(out, pwriter, mTmpMatrix);
+ mNumWrittenViews++;
+ // If the child has children, push it on the stack to write them next.
+ if ((flags&ViewNode.FLAGS_HAS_CHILDREN) != 0) {
+ if (DEBUG_PARCEL_TREE) Log.d(TAG, "Preparing to write "
+ + child.mChildren.length + " children under " + child);
+ out.writeInt(child.mChildren.length);
+ int pos = ++mCurViewStackPos;
+ pushViewStackEntry(child, pos);
+ }
+ return true;
+ }
+
+ // We are done writing children of the current view; pop off the stack.
+ do {
+ int pos = --mCurViewStackPos;
+ if (DEBUG_PARCEL_TREE) Log.d(TAG, "Done with " + mCurViewStackEntry.node
+ + "; popping up to " + pos);
+ if (pos < 0) {
+ // Reached the last view; step to next window.
+ if (DEBUG_PARCEL_TREE) Log.d(TAG, "Done with view hierarchy!");
+ mCurViewStackEntry = null;
+ break;
+ }
+ mCurViewStackEntry = mViewStack.get(pos);
+ } while (mCurViewStackEntry.curChild >= mCurViewStackEntry.numChildren);
+ return true;
+ }
+
+ // Write the next window if appropriate.
+ int pos = mCurWindow;
+ if (pos < mNumWindows) {
+ WindowNode win = as.mWindowNodes.get(pos);
+ mCurWindow++;
+ if (DEBUG_PARCEL) Log.d(TAG, "write window #" + pos + ": at " + out.dataPosition()
+ + ", windows=" + mNumWrittenWindows
+ + ", views=" + mNumWrittenViews);
+ out.writeInt(1);
+ win.writeSelfToParcel(out, pwriter, mTmpMatrix);
+ mNumWrittenWindows++;
+ ViewNode root = win.mRoot;
+ mCurViewStackPos = 0;
+ if (DEBUG_PARCEL_TREE) Log.d(TAG, "Pushing initial root view " + root);
+ pushViewStackEntry(root, 0);
+ return true;
+ }
+
+ return false;
+ }
+ }
+
+ final class ParcelTransferReader {
+ final float[] mTmpMatrix = new float[9];
+ PooledStringReader mStringReader;
+
+ int mNumReadWindows;
+ int mNumReadViews;
+
+ private final IBinder mChannel;
+ private IBinder mTransferToken;
+ private Parcel mCurParcel;
+
+ ParcelTransferReader(IBinder channel) {
+ mChannel = channel;
+ }
+
+ void go() {
+ fetchData();
+ mActivityComponent = ComponentName.readFromParcel(mCurParcel);
+ final int N = mCurParcel.readInt();
+ if (N > 0) {
+ if (DEBUG_PARCEL) Log.d(TAG, "Creating PooledStringReader @ "
+ + mCurParcel.dataPosition());
+ mStringReader = new PooledStringReader(mCurParcel);
+ if (DEBUG_PARCEL) Log.d(TAG, "PooledStringReader size = "
+ + mStringReader.getStringCount());
+ for (int i=0; i<N; i++) {
+ mWindowNodes.add(new WindowNode(this));
+ }
+ }
+ if (DEBUG_PARCEL) Log.d(TAG, "Finished reading: at " + mCurParcel.dataPosition()
+ + ", avail=" + mCurParcel.dataAvail() + ", windows=" + mNumReadWindows
+ + ", views=" + mNumReadViews);
+ }
+
+ Parcel readParcel() {
+ if (DEBUG_PARCEL) Log.d(TAG, "readParcel: at " + mCurParcel.dataPosition()
+ + ", avail=" + mCurParcel.dataAvail() + ", windows=" + mNumReadWindows
+ + ", views=" + mNumReadViews);
+ if (mCurParcel.readInt() != 0) {
+ return mCurParcel;
+ }
+ // We have run out of partial data, need to read another batch.
+ mTransferToken = mCurParcel.readStrongBinder();
+ if (mTransferToken == null) {
+ throw new IllegalStateException(
+ "Reached end of partial data without transfer token");
+ }
+ if (DEBUG_PARCEL) Log.d(TAG, "Ran out of partial data at "
+ + mCurParcel.dataPosition() + ", token " + mTransferToken);
+ fetchData();
+ if (DEBUG_PARCEL) Log.d(TAG, "Creating PooledStringReader @ "
+ + mCurParcel.dataPosition());
+ mStringReader = new PooledStringReader(mCurParcel);
+ if (DEBUG_PARCEL) Log.d(TAG, "PooledStringReader size = "
+ + mStringReader.getStringCount());
+ if (DEBUG_PARCEL) Log.d(TAG, "readParcel: at " + mCurParcel.dataPosition()
+ + ", avail=" + mCurParcel.dataAvail() + ", windows=" + mNumReadWindows
+ + ", views=" + mNumReadViews);
+ mCurParcel.readInt();
+ return mCurParcel;
+ }
+
+ private void fetchData() {
+ Parcel data = Parcel.obtain();
+ data.writeInterfaceToken(DESCRIPTOR);
+ data.writeStrongBinder(mTransferToken);
+ if (DEBUG_PARCEL) Log.d(TAG, "Requesting data with token " + mTransferToken);
+ if (mCurParcel != null) {
+ mCurParcel.recycle();
+ }
+ mCurParcel = Parcel.obtain();
+ try {
+ mChannel.transact(TRANSACTION_XFER, data, mCurParcel, 0);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failure reading AssistStructure data", e);
+ throw new IllegalStateException("Failure reading AssistStructure data: " + e);
+ }
+ data.recycle();
+ mNumReadWindows = mNumReadViews = 0;
+ }
+ }
+
final static class ViewNodeText {
CharSequence mText;
float mTextSize;
@@ -145,24 +405,25 @@ public class AssistStructure implements Parcelable {
view.dispatchProvideStructure(builder);
}
- WindowNode(Parcel in, PooledStringReader preader, float[] tmpMatrix) {
+ WindowNode(ParcelTransferReader reader) {
+ Parcel in = reader.readParcel();
+ reader.mNumReadWindows++;
mX = in.readInt();
mY = in.readInt();
mWidth = in.readInt();
mHeight = in.readInt();
mTitle = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
mDisplayId = in.readInt();
- mRoot = new ViewNode(in, preader, tmpMatrix);
+ mRoot = new ViewNode(reader);
}
- int writeToParcel(Parcel out, PooledStringWriter pwriter, float[] tmpMatrix) {
+ void writeSelfToParcel(Parcel out, PooledStringWriter pwriter, float[] tmpMatrix) {
out.writeInt(mX);
out.writeInt(mY);
out.writeInt(mWidth);
out.writeInt(mHeight);
TextUtils.writeToParcel(mTitle, out, 0);
out.writeInt(mDisplayId);
- return mRoot.writeToParcel(out, pwriter, tmpMatrix);
}
/**
@@ -287,7 +548,10 @@ public class AssistStructure implements Parcelable {
ViewNode() {
}
- ViewNode(Parcel in, PooledStringReader preader, float[] tmpMatrix) {
+ ViewNode(ParcelTransferReader reader) {
+ final Parcel in = reader.readParcel();
+ reader.mNumReadViews++;
+ final PooledStringReader preader = reader.mStringReader;
mClassName = preader.readString();
mFlags = in.readInt();
final int flags = mFlags;
@@ -320,8 +584,8 @@ public class AssistStructure implements Parcelable {
}
if ((flags&FLAGS_HAS_MATRIX) != 0) {
mMatrix = new Matrix();
- in.readFloatArray(tmpMatrix);
- mMatrix.setValues(tmpMatrix);
+ in.readFloatArray(reader.mTmpMatrix);
+ mMatrix.setValues(reader.mTmpMatrix);
}
if ((flags&FLAGS_HAS_ELEVATION) != 0) {
mElevation = in.readFloat();
@@ -342,12 +606,12 @@ public class AssistStructure implements Parcelable {
final int NCHILDREN = in.readInt();
mChildren = new ViewNode[NCHILDREN];
for (int i=0; i<NCHILDREN; i++) {
- mChildren[i] = new ViewNode(in, preader, tmpMatrix);
+ mChildren[i] = new ViewNode(reader);
}
}
}
- int writeToParcel(Parcel out, PooledStringWriter pwriter, float[] tmpMatrix) {
+ int writeSelfToParcel(Parcel out, PooledStringWriter pwriter, float[] tmpMatrix) {
int flags = mFlags & ~FLAGS_ALL_CONTROL;
if (mId != View.NO_ID) {
flags |= FLAGS_HAS_ID;
@@ -428,15 +692,7 @@ public class AssistStructure implements Parcelable {
if ((flags&FLAGS_HAS_EXTRAS) != 0) {
out.writeBundle(mExtras);
}
- int N = 1;
- if ((flags&FLAGS_HAS_CHILDREN) != 0) {
- final int NCHILDREN = mChildren.length;
- out.writeInt(NCHILDREN);
- for (int i=0; i<NCHILDREN; i++) {
- N += mChildren[i].writeToParcel(out, pwriter, tmpMatrix);
- }
- }
- return N;
+ return flags;
}
/**
@@ -1177,22 +1433,11 @@ public class AssistStructure implements Parcelable {
return;
}
mHaveData = true;
- Parcel data = Parcel.obtain();
- Parcel reply = Parcel.obtain();
- data.writeInterfaceToken(DESCRIPTOR);
- try {
- mReceiveChannel.transact(TRANSACTION_XFER, data, reply, 0);
- } catch (RemoteException e) {
- Log.w(TAG, "Failure reading AssistStructure data", e);
- return;
- }
- readContentFromParcel(reply);
- data.recycle();
- reply.recycle();
+ ParcelTransferReader reader = new ParcelTransferReader(mReceiveChannel);
+ reader.go();
}
- void writeContentToParcel(Parcel out, int flags) {
- // First make sure all content has been created.
+ boolean waitForReady() {
boolean skipStructure = false;
synchronized (this) {
long endTime = SystemClock.uptimeMillis() + 5000;
@@ -1210,30 +1455,14 @@ public class AssistStructure implements Parcelable {
skipStructure = true;
}
}
- int start = out.dataPosition();
- PooledStringWriter pwriter = new PooledStringWriter(out);
- float[] tmpMatrix = new float[9];
- ComponentName.writeToParcel(mActivityComponent, out);
- final int N = skipStructure ? 0 : mWindowNodes.size();
- out.writeInt(N);
- int NV = 0;
- for (int i=0; i<N; i++) {
- NV += mWindowNodes.get(i).writeToParcel(out, pwriter, tmpMatrix);
- }
- pwriter.finish();
- Log.i(TAG, "Flattened assist data: " + (out.dataPosition() - start) + " bytes, containing "
- + N + " windows, " + NV + " views");
+ return !skipStructure;
}
- void readContentFromParcel(Parcel in) {
- PooledStringReader preader = new PooledStringReader(in);
- float[] tmpMatrix = new float[9];
- mActivityComponent = ComponentName.readFromParcel(in);
- final int N = in.readInt();
- for (int i=0; i<N; i++) {
- mWindowNodes.add(new WindowNode(in, preader, tmpMatrix));
+ /** @hide */
+ public void clearSendChannel() {
+ if (mSendChannel != null) {
+ mSendChannel.mAssistStructure = null;
}
- //dump();
}
public int describeContents() {
@@ -1245,7 +1474,7 @@ public class AssistStructure implements Parcelable {
// 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();
+ mSendChannel = new SendChannel(this);
}
out.writeStrongBinder(mSendChannel);
} else {
diff --git a/core/java/android/bluetooth/BluetoothSocket.java b/core/java/android/bluetooth/BluetoothSocket.java
index 6302521..fb81fd1 100644
--- a/core/java/android/bluetooth/BluetoothSocket.java
+++ b/core/java/android/bluetooth/BluetoothSocket.java
@@ -240,6 +240,8 @@ public final class BluetoothSocket implements Closeable {
as.close();
throw new IOException("bt socket acept failed");
}
+
+ as.mPfd = new ParcelFileDescriptor(fds[0]);
as.mSocket = new LocalSocket(fds[0]);
as.mSocketIS = as.mSocket.getInputStream();
as.mSocketOS = as.mSocket.getOutputStream();
diff --git a/core/java/android/bluetooth/IBluetoothGatt.aidl b/core/java/android/bluetooth/IBluetoothGatt.aidl
index 72abeaf..3660be7 100644
--- a/core/java/android/bluetooth/IBluetoothGatt.aidl
+++ b/core/java/android/bluetooth/IBluetoothGatt.aidl
@@ -36,7 +36,7 @@ interface IBluetoothGatt {
void startScan(in int appIf, in boolean isServer, in ScanSettings settings,
in List<ScanFilter> filters,
- in List scanStorages);
+ in List scanStorages, in String callingPackage);
void stopScan(in int appIf, in boolean isServer);
void flushPendingBatchResults(in int appIf, in boolean isServer);
void startMultiAdvertising(in int appIf,
diff --git a/core/java/android/bluetooth/le/BluetoothLeScanner.java b/core/java/android/bluetooth/le/BluetoothLeScanner.java
index 2e6c4f0..e09ab56 100644
--- a/core/java/android/bluetooth/le/BluetoothLeScanner.java
+++ b/core/java/android/bluetooth/le/BluetoothLeScanner.java
@@ -19,6 +19,7 @@ package android.bluetooth.le;
import android.Manifest;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
+import android.app.ActivityThread;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallbackWrapper;
@@ -313,7 +314,7 @@ public final class BluetoothLeScanner {
mClientIf = clientIf;
try {
mBluetoothGatt.startScan(mClientIf, false, mSettings, mFilters,
- mResultStorages);
+ mResultStorages, ActivityThread.currentOpPackageName());
} catch (RemoteException e) {
Log.e(TAG, "fail to start le scan: " + e);
mClientIf = -1;
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 2b83d86..ceb610a 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -502,8 +502,7 @@ interface IPackageManager {
void addOnPermissionsChangeListener(in IOnPermissionsChangeListener listener);
void removeOnPermissionsChangeListener(in IOnPermissionsChangeListener listener);
-
- int getMountExternalMode(int uid);
-
void grantDefaultPermissionsToEnabledCarrierApps(in String[] packageNames, int userId);
+
+ boolean isPermissionRevokedByPolicy(String permission, String packageName, int userId);
}
diff --git a/core/java/android/content/pm/IntentFilterVerificationInfo.java b/core/java/android/content/pm/IntentFilterVerificationInfo.java
index 96000dd..4dbac05 100644
--- a/core/java/android/content/pm/IntentFilterVerificationInfo.java
+++ b/core/java/android/content/pm/IntentFilterVerificationInfo.java
@@ -26,7 +26,9 @@ import android.os.Parcelable;
import android.text.TextUtils;
import android.util.ArraySet;
import android.util.Log;
+
import com.android.internal.util.XmlUtils;
+
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
@@ -181,14 +183,28 @@ public final class IntentFilterVerificationInfo implements Parcelable {
return getStatusStringFromValue(mMainStatus);
}
- public static String getStatusStringFromValue(int val) {
- switch (val) {
- case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK : return "ask";
- case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS : return "always";
- case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER : return "never";
+ public static String getStatusStringFromValue(long val) {
+ StringBuilder sb = new StringBuilder();
+ switch ((int)(val >> 32)) {
+ case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS:
+ sb.append("always : ");
+ sb.append(Long.toHexString(val & 0x00000000FFFFFFFF));
+ break;
+
+ case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ASK:
+ sb.append("ask");
+ break;
+
+ case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_NEVER:
+ sb.append("never");
+ break;
+
+ case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED:
default:
- case INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED : return "undefined";
+ sb.append("undefined");
+ break;
}
+ return sb.toString();
}
@Override
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 62c2e8c..6533bbc 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -2398,7 +2398,7 @@ public abstract class PackageManager {
* Check whether a particular package has been granted a particular
* permission.
*
- * @param permName The name of the permission you are checking for,
+ * @param permName The name of the permission you are checking for.
* @param pkgName The name of the package you are checking against.
*
* @return If the package has the permission, PERMISSION_GRANTED is
@@ -2412,6 +2412,21 @@ public abstract class PackageManager {
public abstract int checkPermission(String permName, String pkgName);
/**
+ * Checks whether a particular permissions has been revoked for a
+ * package by policy. Typically the device owner or the profile owner
+ * may apply such a policy. The user cannot grant policy revoked
+ * permissions, hence the only way for an app to get such a permission
+ * is by a policy change.
+ *
+ * @param permName The name of the permission you are checking for.
+ * @param pkgName The name of the package you are checking against.
+ *
+ * @return Whether the permission is restricted by policy.
+ */
+ @CheckResult
+ public abstract boolean isPermissionRevokedByPolicy(String permName, String pkgName);
+
+ /**
* Add a new dynamic permission to the system. For this to work, your
* package must have defined a permission tree through the
* {@link android.R.styleable#AndroidManifestPermissionTree
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 48ffb98..ed7a2a3 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -2835,7 +2835,6 @@ public class PackageParser {
if (!aii.hasAction(Intent.ACTION_DEFAULT)) continue;
if (aii.hasDataScheme(IntentFilter.SCHEME_HTTP) ||
aii.hasDataScheme(IntentFilter.SCHEME_HTTPS)) {
- Slog.d(TAG, "hasDomainURLs:true for package:" + pkg.packageName);
return true;
}
}
diff --git a/core/java/android/content/pm/PackageUserState.java b/core/java/android/content/pm/PackageUserState.java
index 92b8055..9b28401 100644
--- a/core/java/android/content/pm/PackageUserState.java
+++ b/core/java/android/content/pm/PackageUserState.java
@@ -38,6 +38,7 @@ public class PackageUserState {
public ArraySet<String> enabledComponents;
public int domainVerificationStatus;
+ public int appLinkGeneration;
public PackageUserState() {
installed = true;
@@ -60,5 +61,6 @@ public class PackageUserState {
? new ArraySet<>(o.enabledComponents) : null;
blockUninstall = o.blockUninstall;
domainVerificationStatus = o.domainVerificationStatus;
+ appLinkGeneration = o.appLinkGeneration;
}
}
diff --git a/core/java/android/content/res/ColorStateList.java b/core/java/android/content/res/ColorStateList.java
index 579634f..19921b5 100644
--- a/core/java/android/content/res/ColorStateList.java
+++ b/core/java/android/content/res/ColorStateList.java
@@ -603,7 +603,7 @@ public class ColorStateList implements Parcelable {
* @hide only for resource preloading
*/
public ConstantState<ColorStateList> getConstantState() {
- if (mFactory != null) {
+ if (mFactory == null) {
mFactory = new ColorStateListFactory(this);
}
return mFactory;
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index 9a99a46..731903c 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -1918,6 +1918,7 @@ public class Resources {
other.mResId = mResId;
other.mForce = mForce;
other.mCount = mCount;
+ other.mHashCode = mHashCode;
return other;
}
}
diff --git a/core/java/android/hardware/SystemSensorManager.java b/core/java/android/hardware/SystemSensorManager.java
index e7deae8..30cdfd3 100644
--- a/core/java/android/hardware/SystemSensorManager.java
+++ b/core/java/android/hardware/SystemSensorManager.java
@@ -123,7 +123,10 @@ public class SystemSensorManager extends SensorManager {
SensorEventQueue queue = mSensorListeners.get(listener);
if (queue == null) {
Looper looper = (handler != null) ? handler.getLooper() : mMainLooper;
- queue = new SensorEventQueue(listener, looper, this);
+ final String fullClassName = listener.getClass().getEnclosingClass() != null ?
+ listener.getClass().getEnclosingClass().getName() :
+ listener.getClass().getName();
+ queue = new SensorEventQueue(listener, looper, this, fullClassName);
if (!queue.addSensor(sensor, delayUs, maxBatchReportLatencyUs)) {
queue.dispose();
return false;
@@ -166,12 +169,17 @@ public class SystemSensorManager extends SensorManager {
protected boolean requestTriggerSensorImpl(TriggerEventListener listener, Sensor sensor) {
if (sensor == null) throw new IllegalArgumentException("sensor cannot be null");
+ if (listener == null) throw new IllegalArgumentException("listener cannot be null");
+
if (sensor.getReportingMode() != Sensor.REPORTING_MODE_ONE_SHOT) return false;
synchronized (mTriggerListeners) {
TriggerEventQueue queue = mTriggerListeners.get(listener);
if (queue == null) {
- queue = new TriggerEventQueue(listener, mMainLooper, this);
+ final String fullClassName = listener.getClass().getEnclosingClass() != null ?
+ listener.getClass().getEnclosingClass().getName() :
+ listener.getClass().getName();
+ queue = new TriggerEventQueue(listener, mMainLooper, this, fullClassName);
if (!queue.addSensor(sensor, 0, 0)) {
queue.dispose();
return false;
@@ -234,7 +242,8 @@ public class SystemSensorManager extends SensorManager {
}
// Initialize a client for data_injection.
if (mInjectEventQueue == null) {
- mInjectEventQueue = new InjectEventQueue(mMainLooper, this);
+ mInjectEventQueue = new InjectEventQueue(mMainLooper, this,
+ mContext.getPackageName());
}
} else {
// If data injection is being disabled clean up the native resources.
@@ -296,10 +305,11 @@ public class SystemSensorManager extends SensorManager {
protected static final int OPERATING_MODE_NORMAL = 0;
protected static final int OPERATING_MODE_DATA_INJECTION = 1;
- BaseEventQueue(Looper looper, SystemSensorManager manager, int mode) {
+ BaseEventQueue(Looper looper, SystemSensorManager manager, int mode, String packageName) {
+ if (packageName == null) packageName = "";
nSensorEventQueue = nativeInitBaseEventQueue(manager.mNativeInstance,
new WeakReference<>(this), looper.getQueue(), mScratch,
- manager.mContext.getPackageName(), mode, manager.mContext.getOpPackageName());
+ packageName, mode, manager.mContext.getOpPackageName());
mCloseGuard.open("dispose");
mManager = manager;
}
@@ -419,8 +429,8 @@ public class SystemSensorManager extends SensorManager {
private final SparseArray<SensorEvent> mSensorsEvents = new SparseArray<SensorEvent>();
public SensorEventQueue(SensorEventListener listener, Looper looper,
- SystemSensorManager manager) {
- super(looper, manager, OPERATING_MODE_NORMAL);
+ SystemSensorManager manager, String packageName) {
+ super(looper, manager, OPERATING_MODE_NORMAL, packageName);
mListener = listener;
}
@@ -486,8 +496,8 @@ public class SystemSensorManager extends SensorManager {
private final SparseArray<TriggerEvent> mTriggerEvents = new SparseArray<TriggerEvent>();
public TriggerEventQueue(TriggerEventListener listener, Looper looper,
- SystemSensorManager manager) {
- super(looper, manager, OPERATING_MODE_NORMAL);
+ SystemSensorManager manager, String packageName) {
+ super(looper, manager, OPERATING_MODE_NORMAL, packageName);
mListener = listener;
}
@@ -540,8 +550,8 @@ public class SystemSensorManager extends SensorManager {
}
final class InjectEventQueue extends BaseEventQueue {
- public InjectEventQueue(Looper looper, SystemSensorManager manager) {
- super(looper, manager, OPERATING_MODE_DATA_INJECTION);
+ public InjectEventQueue(Looper looper, SystemSensorManager manager, String packageName) {
+ super(looper, manager, OPERATING_MODE_DATA_INJECTION, packageName);
}
int injectSensorData(int handle, float[] values,int accuracy, long timestamp) {
diff --git a/core/java/android/hardware/camera2/CameraCaptureSession.java b/core/java/android/hardware/camera2/CameraCaptureSession.java
index 62ebfb3..46cafad 100644
--- a/core/java/android/hardware/camera2/CameraCaptureSession.java
+++ b/core/java/android/hardware/camera2/CameraCaptureSession.java
@@ -482,17 +482,6 @@ public abstract class CameraCaptureSession implements AutoCloseable {
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 547cce4..30aa2d5 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -664,7 +664,7 @@ public final class CameraCharacteristics extends CameraMetadata<CameraCharacteri
* 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 }.
+ * {@link android.hardware.camera2.CameraConstrainedHighSpeedCaptureSession#createHighSpeedRequestList }.
* 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,
diff --git a/core/java/android/hardware/camera2/CameraConstrainedHighSpeedCaptureSession.java b/core/java/android/hardware/camera2/CameraConstrainedHighSpeedCaptureSession.java
new file mode 100644
index 0000000..07d2443
--- /dev/null
+++ b/core/java/android/hardware/camera2/CameraConstrainedHighSpeedCaptureSession.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.hardware.camera2;
+
+import android.annotation.NonNull;
+import android.hardware.camera2.params.StreamConfigurationMap;
+
+import java.util.List;
+
+/**
+ * A constrained high speed capture session for a {@link CameraDevice}, used for capturing high
+ * speed images from the {@link CameraDevice} for high speed video recording use case.
+ * <p>
+ * A CameraHighSpeedCaptureSession is created by providing a set of target output surfaces to
+ * {@link CameraDevice#createConstrainedHighSpeedCaptureSession}, Once created, the session is
+ * active until a new session is created by the camera device, or the camera device is closed.
+ * </p>
+ * <p>
+ * An active high speed capture session is 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}). It only
+ * accepts request lists created via {@link #createHighSpeedRequestList}, and the request list can
+ * only be submitted to this session via {@link CameraCaptureSession#captureBurst captureBurst}, or
+ * {@link CameraCaptureSession#setRepeatingBurst setRepeatingBurst}. See
+ * {@link CameraDevice#createConstrainedHighSpeedCaptureSession} for more details of the
+ * limitations.
+ * </p>
+ * <p>
+ * Creating a session is an expensive operation and can take several hundred milliseconds, since it
+ * requires configuring the camera device's internal pipelines and allocating memory buffers for
+ * sending images to the desired targets. Therefore the setup is done asynchronously, and
+ * {@link CameraDevice#createConstrainedHighSpeedCaptureSession} will send the ready-to-use
+ * CameraCaptureSession to the provided listener's
+ * {@link CameraCaptureSession.StateCallback#onConfigured} callback. If configuration cannot be
+ * completed, then the {@link CameraCaptureSession.StateCallback#onConfigureFailed} is called, and
+ * the session will not become active.
+ * </p>
+ * <!--
+ * <p>
+ * Any capture requests (repeating or non-repeating) submitted before the session is ready will be
+ * queued up and will begin capture once the session becomes ready. In case the session cannot be
+ * configured and {@link CameraCaptureSession.StateCallback#onConfigureFailed onConfigureFailed} is
+ * called, all queued capture requests are discarded. </p>
+ * -->
+ * <p>
+ * If a new session is created by the camera device, then the previous session is closed, and its
+ * associated {@link CameraCaptureSession.StateCallback#onClosed onClosed} callback will be
+ * invoked. All of the session methods will throw an IllegalStateException if called once the
+ * session is closed.
+ * </p>
+ * <p>
+ * A closed session clears any repeating requests (as if {@link #stopRepeating} had been called),
+ * but will still complete all of its in-progress capture requests as normal, before a newly created
+ * session takes over and reconfigures the camera device.
+ * </p>
+ */
+public abstract class CameraConstrainedHighSpeedCaptureSession extends CameraCaptureSession {
+
+ /**
+ * <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 pressure 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 constrained 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 frame rate 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 CameraDevice#createConstrainedHighSpeedCaptureSession
+ * @see CaptureRequest#CONTROL_AE_TARGET_FPS_RANGE
+ * @see android.hardware.camera2.params.StreamConfigurationMap#getHighSpeedVideoSizes
+ * @see android.hardware.camera2.params.StreamConfigurationMap#getHighSpeedVideoFpsRangesFor
+ * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
+ * @see CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_CONSTRAINED_HIGH_SPEED_VIDEO
+ */
+ @NonNull
+ public abstract List<CaptureRequest> createHighSpeedRequestList(
+ @NonNull CaptureRequest request) throws CameraAccessException;
+
+}
diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java
index 639c8b1..4a71aa0 100644
--- a/core/java/android/hardware/camera2/CameraDevice.java
+++ b/core/java/android/hardware/camera2/CameraDevice.java
@@ -613,8 +613,9 @@ public abstract class CameraDevice implements AutoCloseable {
* 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 CameraConstrainedHighSpeedCaptureSession#createHighSpeedRequestList}, 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
@@ -661,71 +662,13 @@ public abstract class CameraDevice implements AutoCloseable {
* @see CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_CONSTRAINED_HIGH_SPEED_VIDEO
* @see CameraCaptureSession#captureBurst
* @see CameraCaptureSession#setRepeatingBurst
- * @see #createConstrainedHighSpeedRequestList
+ * @see CameraConstrainedHighSpeedCaptureSession#createHighSpeedRequestList
*/
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
diff --git a/core/java/android/hardware/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index 0aa6447..c36683b 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -655,8 +655,8 @@ public abstract class CameraMetadata<TKey> {
* <p>The device supports constrained high speed video recording (frame rate &gt;=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>
+ * only accepts high speed request lists created by
+ * {@link android.hardware.camera2.CameraConstrainedHighSpeedCaptureSession#createHighSpeedRequestList }.</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
@@ -717,9 +717,9 @@ public abstract class CameraMetadata<TKey> {
* <li>The FPS ranges are selected from
* {@link android.hardware.camera2.params.StreamConfigurationMap#getHighSpeedVideoFpsRanges }.</li>
* </ul>
- * <p>When above conditions are NOT satistied, the
+ * <p>When above conditions are NOT satistied,
* {@link android.hardware.camera2.CameraDevice#createConstrainedHighSpeedCaptureSession }
- * and {@link android.hardware.camera2.CameraDevice#createConstrainedHighSpeedRequestList } will fail.</p>
+ * 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
@@ -1813,9 +1813,8 @@ public abstract class CameraMetadata<TKey> {
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 }
+ * <p>This is deprecated, please use {@link android.hardware.camera2.CameraDevice#createConstrainedHighSpeedCaptureSession }
+ * and {@link android.hardware.camera2.CameraConstrainedHighSpeedCaptureSession#createHighSpeedRequestList }
* for high speed video recording.</p>
* <p>Optimized for high speed video recording (frame rate &gt;=60fps) use case.</p>
* <p>The supported high speed video sizes and fps ranges are specified in
@@ -1936,21 +1935,21 @@ public abstract class CameraMetadata<TKey> {
/**
* <p>Same as FACE_PRIORITY scene mode, except that the camera
- * device will choose higher sensivity values ({@link CaptureRequest#SENSOR_SENSITIVITY android.sensor.sensitivity})
+ * device will choose higher sensitivity values ({@link CaptureRequest#SENSOR_SENSITIVITY android.sensor.sensitivity})
* under low light conditions.</p>
* <p>The camera device may be tuned to expose the images in a reduced
* sensitivity range to produce the best quality images. For example,
* if the {@link CameraCharacteristics#SENSOR_INFO_SENSITIVITY_RANGE android.sensor.info.sensitivityRange} gives range of [100, 1600],
* the camera device auto-exposure routine tuning process may limit the actual
- * exposure sensivity range to [100, 1200] to ensure that the noise level isn't
- * exessive to compromise the image quality. Under this situation, the image under
+ * exposure sensitivity range to [100, 1200] to ensure that the noise level isn't
+ * exessive in order to preserve the image quality. Under this situation, the image under
* low light may be under-exposed when the sensor max exposure time (bounded by the
* {@link CaptureRequest#CONTROL_AE_TARGET_FPS_RANGE android.control.aeTargetFpsRange} when {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode} is one of the
- * ON_* modes) and effecitve max sensitivity are reached. This scene mode allows the
+ * ON_* modes) and effective max sensitivity are reached. This scene mode allows the
* camera device auto-exposure routine to increase the sensitivity up to the max
* sensitivity specified by {@link CameraCharacteristics#SENSOR_INFO_SENSITIVITY_RANGE android.sensor.info.sensitivityRange} when the scene is too
* dark and the max exposure time is reached. The captured images may be noisier
- * compared with the images captured in normal FACE_PRIORITY mode, therefore, it is
+ * compared with the images captured in normal FACE_PRIORITY mode; therefore, it is
* recommended that the application only use this scene mode when it is capable of
* reducing the noise level of the captured images.</p>
* <p>Unlike the other scene modes, {@link CaptureRequest#CONTROL_AE_MODE android.control.aeMode},
diff --git a/core/java/android/hardware/camera2/CaptureRequest.java b/core/java/android/hardware/camera2/CaptureRequest.java
index 6d8cc54..a136d0f 100644
--- a/core/java/android/hardware/camera2/CaptureRequest.java
+++ b/core/java/android/hardware/camera2/CaptureRequest.java
@@ -184,7 +184,7 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
private final CameraMetadataNative mSettings;
private boolean mIsReprocess;
// If this request is part of constrained high speed request list that was created by
- // {@link CameraDevice#createConstrainedHighSpeedRequestList}.
+ // {@link android.hardware.camera2.CameraConstrainedHighSpeedCaptureSession#createHighSpeedRequestList}
private boolean mIsPartOfCHSRequestList = false;
// Each reprocess request must be tied to a reprocessable session ID.
// Valid only for reprocess requests (mIsReprocess == true).
@@ -340,14 +340,14 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
/**
* <p>Determine if this request is part of a constrained high speed request list that was
- * created by {@link CameraDevice#createConstrainedHighSpeedRequestList}. A constrained high
- * speed request list contains some constrained high speed capture requests with certain
- * interleaved pattern that is suitable for high speed preview/video streaming. An active
- * constrained high speed capture session only accepts constrained high speed request lists.
- * This method can be used to do the sanity check when a constrained high speed capture session
- * receives a request list via {@link CameraCaptureSession#setRepeatingBurst} or
- * {@link CameraCaptureSession#captureBurst}.
- * </p>
+ * created by
+ * {@link android.hardware.camera2.CameraConstrainedHighSpeedCaptureSession#createHighSpeedRequestList}.
+ * A constrained high speed request list contains some constrained high speed capture requests
+ * with certain interleaved pattern that is suitable for high speed preview/video streaming. An
+ * active constrained high speed capture session only accepts constrained high speed request
+ * lists. This method can be used to do the sanity check when a constrained high speed capture
+ * session receives a request list via {@link CameraCaptureSession#setRepeatingBurst} or
+ * {@link CameraCaptureSession#captureBurst}. </p>
*
*
* @return {@code true} if this request is part of a constrained high speed request list,
@@ -595,9 +595,10 @@ public final class CaptureRequest extends CameraMetadata<CaptureRequest.Key<?>>
/**
* <p>Mark this request as part of a constrained high speed request list created by
- * {@link CameraDevice#createConstrainedHighSpeedRequestList}. A constrained high speed
- * request list contains some constrained high speed capture requests with certain
- * interleaved pattern that is suitable for high speed preview/video streaming.</p>
+ * {@link android.hardware.camera2.CameraConstrainedHighSpeedCaptureSession#createHighSpeedRequestList}.
+ * A constrained high speed request list contains some constrained high speed capture
+ * requests with certain interleaved pattern that is suitable for high speed preview/video
+ * streaming.</p>
*
* @hide
*/
diff --git a/core/java/android/hardware/camera2/impl/CameraCaptureSessionCore.java b/core/java/android/hardware/camera2/impl/CameraCaptureSessionCore.java
new file mode 100644
index 0000000..116f0f1
--- /dev/null
+++ b/core/java/android/hardware/camera2/impl/CameraCaptureSessionCore.java
@@ -0,0 +1,64 @@
+/*
+ * 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.hardware.camera2.impl;
+
+/**
+ * Internal interface for CameraDeviceImpl to CameraCaptureSessionImpl(s) communication
+ */
+public interface CameraCaptureSessionCore {
+
+ /**
+ * Replace this session with another session.
+ *
+ * <p>This is an optimization to avoid unconfiguring and then immediately having to
+ * reconfigure again.</p>
+ *
+ * <p>The semantics are identical to {@link #close}, except that unconfiguring will be skipped.
+ * </p>
+ *
+ * <p>After this call completes, the session will not call any further methods on the camera
+ * device.</p>
+ *
+ * @see CameraCaptureSession#close
+ */
+ void replaceSessionClose();
+
+ /**
+ *
+ * Create an internal state callback, to be invoked on the mDeviceHandler
+ *
+ * <p>It has a few behaviors:
+ * <ul>
+ * <li>Convert device state changes into session state changes.
+ * <li>Keep track of async tasks that the session began (idle, abort).
+ * </ul>
+ * </p>
+ * */
+ CameraDeviceImpl.StateCallbackKK getDeviceStateCallback();
+
+ /**
+ * Whether currently in mid-abort.
+ *
+ * <p>This is used by the implementation to set the capture failure
+ * reason, in lieu of more accurate error codes from the camera service.
+ * Unsynchronized to avoid deadlocks between simultaneous session->device,
+ * device->session calls.</p>
+ *
+ */
+ boolean isAborting();
+
+}
diff --git a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
index 3d261dd..3c19cd2 100644
--- a/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraCaptureSessionImpl.java
@@ -36,7 +36,8 @@ import java.util.List;
import static android.hardware.camera2.impl.CameraDeviceImpl.checkHandler;
import static com.android.internal.util.Preconditions.*;
-public class CameraCaptureSessionImpl extends CameraCaptureSession {
+public class CameraCaptureSessionImpl extends CameraCaptureSession
+ implements CameraCaptureSessionCore {
private static final String TAG = "CameraCaptureSession";
private static final boolean DEBUG = false;
@@ -60,7 +61,6 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession {
private final android.hardware.camera2.impl.CameraDeviceImpl mDeviceImpl;
/** Internal handler; used for all incoming events to preserve total order */
private final Handler mDeviceHandler;
- private final boolean mIsConstrainedHighSpeedSession;
/** Drain Sequence IDs which have been queued but not yet finished with aborted/completed */
private final TaskDrainer<Integer> mSequenceDrainer;
@@ -89,14 +89,13 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession {
CameraCaptureSessionImpl(int id, Surface input, List<Surface> outputs,
CameraCaptureSession.StateCallback callback, Handler stateHandler,
android.hardware.camera2.impl.CameraDeviceImpl deviceImpl,
- Handler deviceStateHandler, boolean configureSuccess, boolean isConstrainedHighSpeed) {
+ Handler deviceStateHandler, boolean configureSuccess) {
if (outputs == null || outputs.isEmpty()) {
throw new IllegalArgumentException("outputs must be a non-null, non-empty list");
} else if (callback == null) {
throw new IllegalArgumentException("callback must not be null");
}
- mIsConstrainedHighSpeedSession = isConstrainedHighSpeed;
mId = id;
mIdString = String.format("Session %d: ", mId);
@@ -136,30 +135,6 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession {
}
}
-
- private boolean isConstrainedHighSpeedRequestList(List<CaptureRequest> requestList) {
- checkCollectionNotEmpty(requestList, "High speed request list");
- for (CaptureRequest request : requestList) {
- if (!request.isPartOfCRequestList()) {
- return false;
- }
- }
- return true;
- }
-
- /**
- * If the session is constrained high speed session, it only accept constrained high speed
- * request list.
- */
- private void checkConstrainedHighSpeedRequestSanity(List<CaptureRequest> requestList) {
- if (mIsConstrainedHighSpeedSession) {
- if (!isConstrainedHighSpeedRequestList(requestList)) {
- throw new IllegalArgumentException("It is only allowed to submit a constrained "
- + "high speed request list to a constrained high speed session!!!");
- }
- }
- }
-
@Override
public CameraDevice getDevice() {
return mDeviceImpl;
@@ -181,10 +156,6 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession {
} else if (request.isReprocess() && request.getReprocessableSessionId() != mId) {
throw new IllegalArgumentException("capture request was created for another session");
}
- if (mIsConstrainedHighSpeedSession) {
- throw new UnsupportedOperationException("Constrained high speed session doesn't support"
- + " this method");
- }
checkNotClosed();
@@ -208,8 +179,6 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession {
throw new IllegalArgumentException("Requests must have at least one element");
}
- checkConstrainedHighSpeedRequestSanity(requests);
-
for (CaptureRequest request : requests) {
if (request.isReprocess()) {
if (!isReprocessable()) {
@@ -244,10 +213,6 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession {
} else if (request.isReprocess()) {
throw new IllegalArgumentException("repeating reprocess requests are not supported");
}
- if (mIsConstrainedHighSpeedSession) {
- throw new UnsupportedOperationException("Constrained high speed session doesn't support"
- + " this method");
- }
checkNotClosed();
@@ -271,8 +236,6 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession {
throw new IllegalArgumentException("requests must have at least one element");
}
- checkConstrainedHighSpeedRequestSanity(requests);
-
for (CaptureRequest r : requests) {
if (r.isReprocess()) {
throw new IllegalArgumentException("repeating reprocess burst requests are not " +
@@ -349,7 +312,8 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession {
*
* @see CameraCaptureSession#close
*/
- synchronized void replaceSessionClose() {
+ @Override
+ public synchronized void replaceSessionClose() {
/*
* In order for creating new sessions to be fast, the new session should be created
* before the old session is closed.
@@ -431,9 +395,9 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession {
* Unsynchronized to avoid deadlocks between simultaneous session->device,
* device->session calls.</p>
*
- * <p>Package-private.</p>
*/
- boolean isAborting() {
+ @Override
+ public boolean isAborting() {
return mAborting;
}
@@ -521,7 +485,8 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession {
* </ul>
* </p>
* */
- CameraDeviceImpl.StateCallbackKK getDeviceStateCallback() {
+ @Override
+ public CameraDeviceImpl.StateCallbackKK getDeviceStateCallback() {
final CameraCaptureSession session = this;
return new CameraDeviceImpl.StateCallbackKK() {
@@ -759,9 +724,4 @@ public class CameraCaptureSessionImpl extends CameraCaptureSession {
}
}
- @Override
- public boolean isConstrainedHighSpeed() {
- return mIsConstrainedHighSpeedSession;
- }
-
}
diff --git a/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java
new file mode 100644
index 0000000..d732535
--- /dev/null
+++ b/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java
@@ -0,0 +1,283 @@
+/*
+ * 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.hardware.camera2.impl;
+
+import android.hardware.camera2.CameraAccessException;
+import android.hardware.camera2.CameraCaptureSession;
+import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.CameraConstrainedHighSpeedCaptureSession;
+import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.params.StreamConfigurationMap;
+import android.hardware.camera2.utils.SurfaceUtils;
+import android.os.Handler;
+import android.util.Range;
+import android.view.Surface;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+
+import static com.android.internal.util.Preconditions.*;
+
+/**
+ * Standard implementation of CameraConstrainedHighSpeedCaptureSession.
+ *
+ * <p>
+ * Mostly just forwards calls to an instance of CameraCaptureSessionImpl,
+ * but implements the few necessary behavior changes and additional methods required
+ * for the constrained high speed speed mode.
+ * </p>
+ */
+
+public class CameraConstrainedHighSpeedCaptureSessionImpl
+ extends CameraConstrainedHighSpeedCaptureSession implements CameraCaptureSessionCore {
+ private final CameraCharacteristics mCharacteristics;
+ private final CameraCaptureSessionImpl mSessionImpl;
+
+ /**
+ * Create a new CameraCaptureSession.
+ *
+ * <p>The camera device must already be in the {@code IDLE} state when this is invoked.
+ * There must be no pending actions
+ * (e.g. no pending captures, no repeating requests, no flush).</p>
+ */
+ CameraConstrainedHighSpeedCaptureSessionImpl(int id, List<Surface> outputs,
+ CameraCaptureSession.StateCallback callback, Handler stateHandler,
+ android.hardware.camera2.impl.CameraDeviceImpl deviceImpl,
+ Handler deviceStateHandler, boolean configureSuccess,
+ CameraCharacteristics characteristics) {
+ mCharacteristics = characteristics;
+ CameraCaptureSession.StateCallback wrapperCallback = new WrapperCallback(callback);
+ mSessionImpl = new CameraCaptureSessionImpl(id, /*input*/null, outputs, wrapperCallback,
+ stateHandler, deviceImpl, deviceStateHandler, configureSuccess);
+ }
+
+ @Override
+ public List<CaptureRequest> createHighSpeedRequestList(CaptureRequest request)
+ throws CameraAccessException {
+ if (request == null) {
+ throw new IllegalArgumentException("Input capture request must not be null");
+ }
+ Collection<Surface> outputSurfaces = request.getTargets();
+ Range<Integer> fpsRange = request.get(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE);
+
+ StreamConfigurationMap config =
+ mCharacteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
+ SurfaceUtils.checkConstrainedHighSpeedSurfaces(outputSurfaces, fpsRange, config);
+
+ // Request list size: to limit the preview to 30fps, need use maxFps/30; to maximize
+ // the preview frame rate, should use maxBatch size for that high speed stream
+ // configuration. We choose the former for now.
+ int requestListSize = fpsRange.getUpper() / 30;
+ List<CaptureRequest> requestList = new ArrayList<CaptureRequest>();
+
+ // Prepare the Request builders: need carry over the request controls.
+ // First, create a request builder that will only include preview or recording target.
+ CameraMetadataNative requestMetadata = new CameraMetadataNative(request.getNativeCopy());
+ // Note that after this step, the requestMetadata is mutated (swapped) and can not be used
+ // for next request builder creation.
+ CaptureRequest.Builder singleTargetRequestBuilder = new CaptureRequest.Builder(
+ requestMetadata, /*reprocess*/false, CameraCaptureSession.SESSION_ID_NONE);
+
+ // Overwrite the capture intent to make sure a good value is set.
+ Iterator<Surface> iterator = outputSurfaces.iterator();
+ Surface firstSurface = iterator.next();
+ Surface secondSurface = null;
+ if (outputSurfaces.size() == 1 && SurfaceUtils.isSurfaceForHwVideoEncoder(firstSurface)) {
+ singleTargetRequestBuilder.set(CaptureRequest.CONTROL_CAPTURE_INTENT,
+ CaptureRequest.CONTROL_CAPTURE_INTENT_PREVIEW);
+ } else {
+ // Video only, or preview + video
+ singleTargetRequestBuilder.set(CaptureRequest.CONTROL_CAPTURE_INTENT,
+ CaptureRequest.CONTROL_CAPTURE_INTENT_VIDEO_RECORD);
+ }
+ singleTargetRequestBuilder.setPartOfCHSRequestList(/*partOfCHSList*/true);
+
+ // Second, Create a request builder that will include both preview and recording targets.
+ CaptureRequest.Builder doubleTargetRequestBuilder = null;
+ if (outputSurfaces.size() == 2) {
+ // Have to create a new copy, the original one was mutated after a new
+ // CaptureRequest.Builder creation.
+ requestMetadata = new CameraMetadataNative(request.getNativeCopy());
+ doubleTargetRequestBuilder = new CaptureRequest.Builder(
+ requestMetadata, /*reprocess*/false, CameraCaptureSession.SESSION_ID_NONE);
+ doubleTargetRequestBuilder.set(CaptureRequest.CONTROL_CAPTURE_INTENT,
+ CaptureRequest.CONTROL_CAPTURE_INTENT_VIDEO_RECORD);
+ doubleTargetRequestBuilder.addTarget(firstSurface);
+ secondSurface = iterator.next();
+ doubleTargetRequestBuilder.addTarget(secondSurface);
+ doubleTargetRequestBuilder.setPartOfCHSRequestList(/*partOfCHSList*/true);
+ // Make sure singleTargetRequestBuilder contains only recording surface for
+ // preview + recording case.
+ Surface recordingSurface = firstSurface;
+ if (!SurfaceUtils.isSurfaceForHwVideoEncoder(recordingSurface)) {
+ recordingSurface = secondSurface;
+ }
+ singleTargetRequestBuilder.addTarget(recordingSurface);
+ } else {
+ // Single output case: either recording or preview.
+ singleTargetRequestBuilder.addTarget(firstSurface);
+ }
+
+ // Generate the final request list.
+ for (int i = 0; i < requestListSize; i++) {
+ if (i == 0 && doubleTargetRequestBuilder != null) {
+ // First request should be recording + preview request
+ requestList.add(doubleTargetRequestBuilder.build());
+ } else {
+ requestList.add(singleTargetRequestBuilder.build());
+ }
+ }
+
+ return Collections.unmodifiableList(requestList);
+ }
+
+ private boolean isConstrainedHighSpeedRequestList(List<CaptureRequest> requestList) {
+ checkCollectionNotEmpty(requestList, "High speed request list");
+ for (CaptureRequest request : requestList) {
+ if (!request.isPartOfCRequestList()) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public CameraDevice getDevice() {
+ return mSessionImpl.getDevice();
+ }
+
+ @Override
+ public void prepare(Surface surface) throws CameraAccessException {
+ mSessionImpl.prepare(surface);
+ }
+
+ @Override
+ public int capture(CaptureRequest request, CaptureCallback listener, Handler handler)
+ throws CameraAccessException {
+ throw new UnsupportedOperationException("Constrained high speed session doesn't support"
+ + " this method");
+ }
+
+ @Override
+ public int captureBurst(List<CaptureRequest> requests, CaptureCallback listener,
+ Handler handler) throws CameraAccessException {
+ if (!isConstrainedHighSpeedRequestList(requests)) {
+ throw new IllegalArgumentException(
+ "Only request lists created by createHighSpeedRequestList() can be submitted to " +
+ "a constrained high speed capture session");
+ }
+ return mSessionImpl.captureBurst(requests, listener, handler);
+ }
+
+ @Override
+ public int setRepeatingRequest(CaptureRequest request, CaptureCallback listener,
+ Handler handler) throws CameraAccessException {
+ throw new UnsupportedOperationException("Constrained high speed session doesn't support"
+ + " this method");
+ }
+
+ @Override
+ public int setRepeatingBurst(List<CaptureRequest> requests, CaptureCallback listener,
+ Handler handler) throws CameraAccessException {
+ if (!isConstrainedHighSpeedRequestList(requests)) {
+ throw new IllegalArgumentException(
+ "Only request lists created by createHighSpeedRequestList() can be submitted to " +
+ "a constrained high speed capture session");
+ }
+ return mSessionImpl.setRepeatingBurst(requests, listener, handler);
+ }
+
+ @Override
+ public void stopRepeating() throws CameraAccessException {
+ mSessionImpl.stopRepeating();
+ }
+
+ @Override
+ public void abortCaptures() throws CameraAccessException {
+ mSessionImpl.abortCaptures();
+ }
+
+ @Override
+ public Surface getInputSurface() {
+ return null;
+ }
+
+ @Override
+ public void close() {
+ mSessionImpl.close();
+ }
+
+ @Override
+ public boolean isReprocessable() {
+ return false;
+ }
+
+ // Implementation of CameraCaptureSessionCore methods
+
+ @Override
+ public void replaceSessionClose() {
+ mSessionImpl.replaceSessionClose();
+ }
+
+ @Override
+ public CameraDeviceImpl.StateCallbackKK getDeviceStateCallback() {
+ return mSessionImpl.getDeviceStateCallback();
+ }
+
+ @Override
+ public boolean isAborting() {
+ return mSessionImpl.isAborting();
+ }
+
+ private class WrapperCallback extends StateCallback {
+ private final StateCallback mCallback;
+
+ public WrapperCallback(StateCallback callback) {
+ mCallback = callback;
+ }
+
+ public void onConfigured(CameraCaptureSession session) {
+ mCallback.onConfigured(CameraConstrainedHighSpeedCaptureSessionImpl.this);
+ }
+
+ public void onConfigureFailed(CameraCaptureSession session) {
+ mCallback.onConfigureFailed(CameraConstrainedHighSpeedCaptureSessionImpl.this);
+ }
+
+ public void onReady(CameraCaptureSession session) {
+ mCallback.onReady(CameraConstrainedHighSpeedCaptureSessionImpl.this);
+ }
+
+ public void onActive(CameraCaptureSession session) {
+ mCallback.onActive(CameraConstrainedHighSpeedCaptureSessionImpl.this);
+ }
+
+ public void onClosed(CameraCaptureSession session) {
+ mCallback.onClosed(CameraConstrainedHighSpeedCaptureSessionImpl.this);
+ }
+
+ public void onSurfacePrepared(CameraCaptureSession session, Surface surface) {
+ mCallback.onSurfacePrepared(CameraConstrainedHighSpeedCaptureSessionImpl.this,
+ surface);
+ }
+
+
+ }
+}
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index 83128c3..c594228 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -113,7 +113,7 @@ public class CameraDeviceImpl extends CameraDevice {
*/
private final FrameNumberTracker mFrameNumberTracker = new FrameNumberTracker();
- private CameraCaptureSessionImpl mCurrentSession;
+ private CameraCaptureSessionCore mCurrentSession;
private int mNextSessionId = 0;
// Runnables for all state transitions, except error, which needs the
@@ -510,6 +510,26 @@ public class CameraDeviceImpl extends CameraDevice {
/*isConstrainedHighSpeed*/false);
}
+ @Override
+ public void createConstrainedHighSpeedCaptureSession(List<Surface> outputs,
+ android.hardware.camera2.CameraCaptureSession.StateCallback callback, Handler handler)
+ throws CameraAccessException {
+ if (outputs == null || outputs.size() == 0 || outputs.size() > 2) {
+ throw new IllegalArgumentException(
+ "Output surface list must not be null and the size must be no more than 2");
+ }
+ StreamConfigurationMap config =
+ getCharacteristics().get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
+ SurfaceUtils.checkConstrainedHighSpeedSurfaces(outputs, /*fpsRange*/null, config);
+
+ List<OutputConfiguration> outConfigurations = new ArrayList<>(outputs.size());
+ for (Surface surface : outputs) {
+ outConfigurations.add(new OutputConfiguration(surface));
+ }
+ createCaptureSessionInternal(null, outConfigurations, callback, handler,
+ /*isConstrainedHighSpeed*/true);
+ }
+
private void createCaptureSessionInternal(InputConfiguration inputConfig,
List<OutputConfiguration> outputConfigurations,
CameraCaptureSession.StateCallback callback, Handler handler,
@@ -565,10 +585,16 @@ public class CameraDeviceImpl extends CameraDevice {
outSurfaces.add(config.getSurface());
}
// Fire onConfigured if configureOutputs succeeded, fire onConfigureFailed otherwise.
- CameraCaptureSessionImpl newSession =
- new CameraCaptureSessionImpl(mNextSessionId++, input,
- outSurfaces, callback, handler, this, mDeviceHandler,
- configureSuccess, isConstrainedHighSpeed);
+ CameraCaptureSessionCore newSession = null;
+ if (isConstrainedHighSpeed) {
+ newSession = new CameraConstrainedHighSpeedCaptureSessionImpl(mNextSessionId++,
+ outSurfaces, callback, handler, this, mDeviceHandler, configureSuccess,
+ mCharacteristics);
+ } else {
+ newSession = new CameraCaptureSessionImpl(mNextSessionId++, input,
+ outSurfaces, callback, handler, this, mDeviceHandler,
+ configureSuccess);
+ }
// TODO: wait until current session closes, then create the new session
mCurrentSession = newSession;
@@ -1933,181 +1959,4 @@ public class CameraDeviceImpl extends CameraDevice {
return mCharacteristics;
}
- /**
- * A high speed output surface can only be preview or hardware encoder surface.
- *
- * @param surface The high speed output surface to be checked.
- */
- private void checkHighSpeedSurfaceFormat(Surface surface) {
- // TODO: remove this override since the default format should be
- // ImageFormat.PRIVATE. b/9487482
- final int HAL_FORMAT_RGB_START = 1; // HAL_PIXEL_FORMAT_RGBA_8888 from graphics.h
- final int HAL_FORMAT_RGB_END = 5; // HAL_PIXEL_FORMAT_BGRA_8888 from graphics.h
- int surfaceFormat = SurfaceUtils.getSurfaceFormat(surface);
- if (surfaceFormat >= HAL_FORMAT_RGB_START &&
- surfaceFormat <= HAL_FORMAT_RGB_END) {
- surfaceFormat = ImageFormat.PRIVATE;
- }
-
- if (surfaceFormat != ImageFormat.PRIVATE) {
- throw new IllegalArgumentException("Surface format(" + surfaceFormat + ") is not"
- + " for preview or hardware video encoding!");
- }
- }
-
- private void checkConstrainedHighSpeedSurfaces(Collection<Surface> surfaces,
- Range<Integer> fpsRange) {
- if (surfaces == null || surfaces.size() == 0 || surfaces.size() > 2) {
- throw new IllegalArgumentException("Output target surface list must not be null and"
- + " the size must be 1 or 2");
- }
-
- StreamConfigurationMap config =
- getCharacteristics().get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
- List<Size> highSpeedSizes = null;
- if (fpsRange == null) {
- highSpeedSizes = Arrays.asList(config.getHighSpeedVideoSizes());
- } else {
- // Check the FPS range first if provided
- Range<Integer>[] highSpeedFpsRanges = config.getHighSpeedVideoFpsRanges();
- if(!Arrays.asList(highSpeedFpsRanges).contains(fpsRange)) {
- throw new IllegalArgumentException("Fps range " + fpsRange.toString() + " in the"
- + " request is not a supported high speed fps range " +
- Arrays.toString(highSpeedFpsRanges));
- }
- highSpeedSizes = Arrays.asList(config.getHighSpeedVideoSizesFor(fpsRange));
- }
-
- for (Surface surface : surfaces) {
- checkHighSpeedSurfaceFormat(surface);
-
- // Surface size must be supported high speed sizes.
- Size surfaceSize = SurfaceUtils.getSurfaceSize(surface);
- if (!highSpeedSizes.contains(surfaceSize)) {
- throw new IllegalArgumentException("Surface size " + surfaceSize.toString() + " is"
- + " not part of the high speed supported size list " +
- Arrays.toString(highSpeedSizes.toArray()));
- }
- // Each output surface must be either preview surface or recording surface.
- if (!SurfaceUtils.isSurfaceForPreview(surface) &&
- !SurfaceUtils.isSurfaceForHwVideoEncoder(surface)) {
- throw new IllegalArgumentException("This output surface is neither preview nor "
- + "hardware video encoding surface");
- }
- if (SurfaceUtils.isSurfaceForPreview(surface) &&
- SurfaceUtils.isSurfaceForHwVideoEncoder(surface)) {
- throw new IllegalArgumentException("This output surface can not be both preview"
- + " and hardware video encoding surface");
- }
- }
-
- // For 2 output surface case, they shouldn't be same type.
- if (surfaces.size() == 2) {
- // Up to here, each surface can only be either preview or recording.
- Iterator<Surface> iterator = surfaces.iterator();
- boolean isFirstSurfacePreview =
- SurfaceUtils.isSurfaceForPreview(iterator.next());
- boolean isSecondSurfacePreview =
- SurfaceUtils.isSurfaceForPreview(iterator.next());
- if (isFirstSurfacePreview == isSecondSurfacePreview) {
- throw new IllegalArgumentException("The 2 output surfaces must have different"
- + " type");
- }
- }
- }
-
- @Override
- public void createConstrainedHighSpeedCaptureSession(List<Surface> outputs,
- android.hardware.camera2.CameraCaptureSession.StateCallback callback, Handler handler)
- throws CameraAccessException {
- if (outputs == null || outputs.size() == 0 || outputs.size() > 2) {
- throw new IllegalArgumentException(
- "Output surface list must not be null and the size must be no more than 2");
- }
- checkConstrainedHighSpeedSurfaces(outputs, /*fpsRange*/null);
-
- List<OutputConfiguration> outConfigurations = new ArrayList<>(outputs.size());
- for (Surface surface : outputs) {
- outConfigurations.add(new OutputConfiguration(surface));
- }
- createCaptureSessionInternal(null, outConfigurations, callback, handler,
- /*isConstrainedHighSpeed*/true);
- }
-
- @Override
- public List<CaptureRequest> createConstrainedHighSpeedRequestList(CaptureRequest request)
- throws CameraAccessException {
- if (request == null) {
- throw new IllegalArgumentException("Input capture request must not be null");
- }
- Collection<Surface> outputSurfaces = request.getTargets();
- Range<Integer> fpsRange = request.get(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE);
- checkConstrainedHighSpeedSurfaces(outputSurfaces, fpsRange);
-
- // Request list size: to limit the preview to 30fps, need use maxFps/30; to maximize
- // the preview frame rate, should use maxBatch size for that high speed stream
- // configuration. We choose the former for now.
- int requestListSize = fpsRange.getUpper() / 30;
- List<CaptureRequest> requestList = new ArrayList<CaptureRequest>();
-
- // Prepare the Request builders: need carry over the request controls.
- // First, create a request builder that will only include preview or recording target.
- CameraMetadataNative requestMetadata = new CameraMetadataNative(request.getNativeCopy());
- // Note that after this step, the requestMetadata is mutated (swapped) and can not be used
- // for next request builder creation.
- CaptureRequest.Builder singleTargetRequestBuilder = new CaptureRequest.Builder(
- requestMetadata, /*reprocess*/false, CameraCaptureSession.SESSION_ID_NONE);
-
- // Overwrite the capture intent to make sure a good value is set.
- Iterator<Surface> iterator = outputSurfaces.iterator();
- Surface firstSurface = iterator.next();
- Surface secondSurface = null;
- if (outputSurfaces.size() == 1 && SurfaceUtils.isSurfaceForHwVideoEncoder(firstSurface)) {
- singleTargetRequestBuilder.set(CaptureRequest.CONTROL_CAPTURE_INTENT,
- CaptureRequest.CONTROL_CAPTURE_INTENT_PREVIEW);
- } else {
- // Video only, or preview + video
- singleTargetRequestBuilder.set(CaptureRequest.CONTROL_CAPTURE_INTENT,
- CaptureRequest.CONTROL_CAPTURE_INTENT_VIDEO_RECORD);
- }
- singleTargetRequestBuilder.setPartOfCHSRequestList(/*partOfCHSList*/true);
-
- // Second, Create a request builder that will include both preview and recording targets.
- CaptureRequest.Builder doubleTargetRequestBuilder = null;
- if (outputSurfaces.size() == 2) {
- // Have to create a new copy, the original one was mutated after a new
- // CaptureRequest.Builder creation.
- requestMetadata = new CameraMetadataNative(request.getNativeCopy());
- doubleTargetRequestBuilder = new CaptureRequest.Builder(
- requestMetadata, /*reprocess*/false, CameraCaptureSession.SESSION_ID_NONE);
- doubleTargetRequestBuilder.set(CaptureRequest.CONTROL_CAPTURE_INTENT,
- CaptureRequest.CONTROL_CAPTURE_INTENT_VIDEO_RECORD);
- doubleTargetRequestBuilder.addTarget(firstSurface);
- secondSurface = iterator.next();
- doubleTargetRequestBuilder.addTarget(secondSurface);
- doubleTargetRequestBuilder.setPartOfCHSRequestList(/*partOfCHSList*/true);
- // Make sure singleTargetRequestBuilder contains only recording surface for
- // preview + recording case.
- Surface recordingSurface = firstSurface;
- if (!SurfaceUtils.isSurfaceForHwVideoEncoder(recordingSurface)) {
- recordingSurface = secondSurface;
- }
- singleTargetRequestBuilder.addTarget(recordingSurface);
- } else {
- // Single output case: either recording or preview.
- singleTargetRequestBuilder.addTarget(firstSurface);
- }
-
- // Generate the final request list.
- for (int i = 0; i < requestListSize; i++) {
- if (i == 0 && doubleTargetRequestBuilder != null) {
- // First request should be recording + preview request
- requestList.add(doubleTargetRequestBuilder.build());
- } else {
- requestList.add(singleTargetRequestBuilder.build());
- }
- }
-
- return Collections.unmodifiableList(requestList);
- }
}
diff --git a/core/java/android/hardware/camera2/params/StreamConfigurationMap.java b/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
index 639ad60..8e0eab2 100644
--- a/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
+++ b/core/java/android/hardware/camera2/params/StreamConfigurationMap.java
@@ -495,7 +495,8 @@ public final class StreamConfigurationMap {
* <p>
* To enable high speed video recording, application must create a constrained create high speed
* capture session via {@link CameraDevice#createConstrainedHighSpeedCaptureSession}, and submit
- * a CaptureRequest list created by {@link CameraDevice#createConstrainedHighSpeedRequestList}
+ * a CaptureRequest list created by
+ * {@link android.hardware.camera2.CameraConstrainedHighSpeedCaptureSession#createHighSpeedRequestList}
* to this session. The application must select the video size from this method and
* {@link CaptureRequest#CONTROL_AE_TARGET_FPS_RANGE FPS range} from
* {@link #getHighSpeedVideoFpsRangesFor} to configure the constrained high speed session and
@@ -506,14 +507,15 @@ public final class StreamConfigurationMap {
* the same size). Otherwise, the high speed session creation will fail. Once the size is
* selected, application can get the supported FPS ranges by
* {@link #getHighSpeedVideoFpsRangesFor}, and use these FPS ranges to setup the recording
- * request lists via {@link CameraDevice#createConstrainedHighSpeedRequestList}.
+ * request lists via
+ * {@link android.hardware.camera2.CameraConstrainedHighSpeedCaptureSession#createHighSpeedRequestList}.
* </p>
*
* @return an array of supported high speed video recording sizes
* @see #getHighSpeedVideoFpsRangesFor(Size)
* @see CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_CONSTRAINED_HIGH_SPEED_VIDEO
* @see CameraDevice#createConstrainedHighSpeedCaptureSession
- * @see CameraDevice#createConstrainedHighSpeedRequestList
+ * @see android.hardware.camera2.CameraConstrainedHighSpeedCaptureSession#createHighSpeedRequestList
*/
public Size[] getHighSpeedVideoSizes() {
Set<Size> keySet = mHighSpeedVideoSizeMap.keySet();
@@ -571,7 +573,8 @@ public final class StreamConfigurationMap {
* <p>
* To enable high speed video recording, application must create a constrained create high speed
* capture session via {@link CameraDevice#createConstrainedHighSpeedCaptureSession}, and submit
- * a CaptureRequest list created by {@link CameraDevice#createConstrainedHighSpeedRequestList}
+ * a CaptureRequest list created by
+ * {@link android.hardware.camera2.CameraConstrainedHighSpeedCaptureSession#createHighSpeedRequestList}
* to this session. The application must select the video size from this method and
* {@link CaptureRequest#CONTROL_AE_TARGET_FPS_RANGE FPS range} from
* {@link #getHighSpeedVideoFpsRangesFor} to configure the constrained high speed session and
@@ -583,7 +586,7 @@ public final class StreamConfigurationMap {
* recording streams must have the same size). Otherwise, the high speed session creation will
* fail. Once the high speed capture session is created, the application can set the FPS range
* in the recording request lists via
- * {@link CameraDevice#createConstrainedHighSpeedRequestList}.
+ * {@link android.hardware.camera2.CameraConstrainedHighSpeedCaptureSession#createHighSpeedRequestList}.
* </p>
* <p>
* The FPS ranges reported by this method will have below characteristics:
@@ -601,7 +604,7 @@ public final class StreamConfigurationMap {
* @see #getHighSpeedVideoSizesFor
* @see CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_CONSTRAINED_HIGH_SPEED_VIDEO
* @see CameraDevice#createConstrainedHighSpeedCaptureSession
- * @see CameraDevice#createConstrainedHighSpeedRequestList
+ * @see CameraDevice#createHighSpeedRequestList
*/
@SuppressWarnings("unchecked")
public Range<Integer>[] getHighSpeedVideoFpsRanges() {
diff --git a/core/java/android/hardware/camera2/utils/SurfaceUtils.java b/core/java/android/hardware/camera2/utils/SurfaceUtils.java
index 064b21a..4b958df 100644
--- a/core/java/android/hardware/camera2/utils/SurfaceUtils.java
+++ b/core/java/android/hardware/camera2/utils/SurfaceUtils.java
@@ -19,9 +19,16 @@ package android.hardware.camera2.utils;
import android.graphics.ImageFormat;
import android.hardware.camera2.legacy.LegacyCameraDevice;
import android.hardware.camera2.legacy.LegacyExceptionUtils.BufferQueueAbandonedException;
+import android.hardware.camera2.params.StreamConfigurationMap;
+import android.util.Range;
import android.util.Size;
import android.view.Surface;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+
/**
* Various Surface utilities.
*/
@@ -105,4 +112,93 @@ public class SurfaceUtils {
return LegacyCameraDevice.isFlexibleConsumer(output);
}
+ /**
+ * A high speed output surface can only be preview or hardware encoder surface.
+ *
+ * @param surface The high speed output surface to be checked.
+ */
+ private static void checkHighSpeedSurfaceFormat(Surface surface) {
+ // TODO: remove this override since the default format should be
+ // ImageFormat.PRIVATE. b/9487482
+ final int HAL_FORMAT_RGB_START = 1; // HAL_PIXEL_FORMAT_RGBA_8888 from graphics.h
+ final int HAL_FORMAT_RGB_END = 5; // HAL_PIXEL_FORMAT_BGRA_8888 from graphics.h
+ int surfaceFormat = SurfaceUtils.getSurfaceFormat(surface);
+ if (surfaceFormat >= HAL_FORMAT_RGB_START &&
+ surfaceFormat <= HAL_FORMAT_RGB_END) {
+ surfaceFormat = ImageFormat.PRIVATE;
+ }
+
+ if (surfaceFormat != ImageFormat.PRIVATE) {
+ throw new IllegalArgumentException("Surface format(" + surfaceFormat + ") is not"
+ + " for preview or hardware video encoding!");
+ }
+ }
+
+ /**
+ * Verify that that the surfaces are valid for high-speed recording mode,
+ * and that the FPS range is supported
+ *
+ * @param surfaces the surfaces to verify as valid in terms of size and format
+ * @param fpsRange the target high-speed FPS range to validate
+ * @param config The stream configuration map for the device in question
+ */
+ public static void checkConstrainedHighSpeedSurfaces(Collection<Surface> surfaces,
+ Range<Integer> fpsRange, StreamConfigurationMap config) {
+ if (surfaces == null || surfaces.size() == 0 || surfaces.size() > 2) {
+ throw new IllegalArgumentException("Output target surface list must not be null and"
+ + " the size must be 1 or 2");
+ }
+
+ List<Size> highSpeedSizes = null;
+ if (fpsRange == null) {
+ highSpeedSizes = Arrays.asList(config.getHighSpeedVideoSizes());
+ } else {
+ // Check the FPS range first if provided
+ Range<Integer>[] highSpeedFpsRanges = config.getHighSpeedVideoFpsRanges();
+ if(!Arrays.asList(highSpeedFpsRanges).contains(fpsRange)) {
+ throw new IllegalArgumentException("Fps range " + fpsRange.toString() + " in the"
+ + " request is not a supported high speed fps range " +
+ Arrays.toString(highSpeedFpsRanges));
+ }
+ highSpeedSizes = Arrays.asList(config.getHighSpeedVideoSizesFor(fpsRange));
+ }
+
+ for (Surface surface : surfaces) {
+ checkHighSpeedSurfaceFormat(surface);
+
+ // Surface size must be supported high speed sizes.
+ Size surfaceSize = SurfaceUtils.getSurfaceSize(surface);
+ if (!highSpeedSizes.contains(surfaceSize)) {
+ throw new IllegalArgumentException("Surface size " + surfaceSize.toString() + " is"
+ + " not part of the high speed supported size list " +
+ Arrays.toString(highSpeedSizes.toArray()));
+ }
+ // Each output surface must be either preview surface or recording surface.
+ if (!SurfaceUtils.isSurfaceForPreview(surface) &&
+ !SurfaceUtils.isSurfaceForHwVideoEncoder(surface)) {
+ throw new IllegalArgumentException("This output surface is neither preview nor "
+ + "hardware video encoding surface");
+ }
+ if (SurfaceUtils.isSurfaceForPreview(surface) &&
+ SurfaceUtils.isSurfaceForHwVideoEncoder(surface)) {
+ throw new IllegalArgumentException("This output surface can not be both preview"
+ + " and hardware video encoding surface");
+ }
+ }
+
+ // For 2 output surface case, they shouldn't be same type.
+ if (surfaces.size() == 2) {
+ // Up to here, each surface can only be either preview or recording.
+ Iterator<Surface> iterator = surfaces.iterator();
+ boolean isFirstSurfacePreview =
+ SurfaceUtils.isSurfaceForPreview(iterator.next());
+ boolean isSecondSurfacePreview =
+ SurfaceUtils.isSurfaceForPreview(iterator.next());
+ if (isFirstSurfacePreview == isSecondSurfacePreview) {
+ throw new IllegalArgumentException("The 2 output surfaces must have different"
+ + " type");
+ }
+ }
+ }
+
}
diff --git a/core/java/android/hardware/usb/IUsbManager.aidl b/core/java/android/hardware/usb/IUsbManager.aidl
index 31a6a96..0fe112c 100644
--- a/core/java/android/hardware/usb/IUsbManager.aidl
+++ b/core/java/android/hardware/usb/IUsbManager.aidl
@@ -82,6 +82,9 @@ interface IUsbManager
/* Clears default preferences and permissions for the package */
void clearDefaults(String packageName, int userId);
+ /* Returns true if the specified USB function is enabled. */
+ boolean isFunctionEnabled(String function);
+
/* Sets the current USB function. */
void setCurrentFunction(String function);
diff --git a/core/java/android/hardware/usb/UsbManager.java b/core/java/android/hardware/usb/UsbManager.java
index c83f466..f58b9d6 100644
--- a/core/java/android/hardware/usb/UsbManager.java
+++ b/core/java/android/hardware/usb/UsbManager.java
@@ -22,7 +22,6 @@ import android.content.Context;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
-import android.os.SystemProperties;
import android.util.Log;
import java.util.HashMap;
@@ -54,8 +53,6 @@ public class UsbManager {
* <li> {@link #USB_CONNECTED} boolean indicating whether USB is connected or disconnected.
* <li> {@link #USB_CONFIGURED} boolean indicating whether USB is configured.
* currently zero if not configured, one for configured.
- * <li> {@link #USB_FUNCTION_MASS_STORAGE} boolean extra indicating whether the
- * mass storage function is enabled
* <li> {@link #USB_FUNCTION_ADB} boolean extra indicating whether the
* adb function is enabled
* <li> {@link #USB_FUNCTION_RNDIS} boolean extra indicating whether the
@@ -152,12 +149,13 @@ public class UsbManager {
public static final String USB_DATA_UNLOCKED = "unlocked";
/**
- * Name of the USB mass storage USB function.
- * Used in extras for the {@link #ACTION_USB_STATE} broadcast
+ * A placeholder indicating that no USB function is being specified.
+ * Used to distinguish between selecting no function vs. the default function in
+ * {@link #setCurrentFunction(String)}.
*
* {@hide}
*/
- public static final String USB_FUNCTION_MASS_STORAGE = "mass_storage";
+ public static final String USB_FUNCTION_NONE = "none";
/**
* Name of the adb USB function.
@@ -218,15 +216,14 @@ public class UsbManager {
/**
* Name of extra for {@link #ACTION_USB_DEVICE_ATTACHED} and
* {@link #ACTION_USB_DEVICE_DETACHED} broadcasts
- * containing the UsbDevice object for the device.
+ * containing the {@link UsbDevice} object for the device.
*/
-
public static final String EXTRA_DEVICE = "device";
/**
* Name of extra for {@link #ACTION_USB_ACCESSORY_ATTACHED} and
* {@link #ACTION_USB_ACCESSORY_DETACHED} broadcasts
- * containing the UsbAccessory object for the accessory.
+ * containing the {@link UsbAccessory} object for the accessory.
*/
public static final String EXTRA_ACCESSORY = "accessory";
@@ -238,23 +235,6 @@ public class UsbManager {
*/
public static final String EXTRA_PERMISSION_GRANTED = "permission";
- /**
- * The persistent property which stores whether adb is enabled or not. Other values are ignored.
- * Previously this value stored non-adb settings, but not anymore.
- * TODO: rename this to something adb specific, rather than using usb.
- *
- * {@hide}
- */
- public static final String ADB_PERSISTENT_PROPERTY = "persist.sys.usb.config";
-
- /**
- * The non-persistent property which stores the current USB settings.
- *
- * {@hide}
- */
- public static final String USB_SETTINGS_PROPERTY = "sys.usb.config";
-
-
private final Context mContext;
private final IUsbManager mService;
@@ -437,31 +417,44 @@ public class UsbManager {
}
}
- private static boolean propertyContainsFunction(String property, String function) {
- String functions = SystemProperties.get(property, "");
- int index = functions.indexOf(function);
- if (index < 0) return false;
- if (index > 0 && functions.charAt(index - 1) != ',') return false;
- int charAfter = index + function.length();
- if (charAfter < functions.length() && functions.charAt(charAfter) != ',') return false;
- return true;
- }
-
/**
- * Returns true if the specified USB function is currently enabled.
+ * Returns true if the specified USB function is currently enabled when in device mode.
+ * <p>
+ * USB functions represent interfaces which are published to the host to access
+ * services offered by the device.
+ * </p>
*
* @param function name of the USB function
- * @return true if the USB function is enabled.
+ * @return true if the USB function is enabled
*
* {@hide}
*/
public boolean isFunctionEnabled(String function) {
- return propertyContainsFunction(USB_SETTINGS_PROPERTY, function);
+ try {
+ return mService.isFunctionEnabled(function);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException in setCurrentFunction", e);
+ return false;
+ }
}
/**
- * Sets the current USB function.
- * If function is null, then the current function is set to the default function.
+ * Sets the current USB function when in device mode.
+ * <p>
+ * USB functions represent interfaces which are published to the host to access
+ * services offered by the device.
+ * </p><p>
+ * This method is intended to select among primary USB functions. The system may
+ * automatically activate additional functions such as {@link #USB_FUNCTION_ADB}
+ * or {@link #USB_FUNCTION_ACCESSORY} based on other settings and states.
+ * </p><p>
+ * The allowed values are: {@link #USB_FUNCTION_NONE}, {@link #USB_FUNCTION_AUDIO_SOURCE},
+ * {@link #USB_FUNCTION_MIDI}, {@link #USB_FUNCTION_MTP}, {@link #USB_FUNCTION_PTP},
+ * or {@link #USB_FUNCTION_RNDIS}.
+ * </p><p>
+ * Note: This function is asynchronous and may fail silently without applying
+ * the requested changes.
+ * </p>
*
* @param function name of the USB function, or null to restore the default function
*
@@ -477,8 +470,9 @@ public class UsbManager {
/**
* Sets whether USB data (for example, MTP exposed pictures) should be made available
- * on the USB connection. Unlocking usb data should only be done with user involvement,
- * since exposing pictures or other data could leak sensitive user information.
+ * on the USB connection when in device mode. Unlocking usb data should only be done with
+ * user involvement, since exposing pictures or other data could leak sensitive
+ * user information.
*
* {@hide}
*/
@@ -491,7 +485,8 @@ public class UsbManager {
}
/**
- * Returns {@code true} iff access to sensitive USB data is currently allowed.
+ * Returns {@code true} iff access to sensitive USB data is currently allowed when
+ * in device mode.
*
* {@hide}
*/
@@ -504,4 +499,51 @@ public class UsbManager {
return false;
}
+ /** @hide */
+ public static String addFunction(String functions, String function) {
+ if ("none".equals(functions)) {
+ return function;
+ }
+ if (!containsFunction(functions, function)) {
+ if (functions.length() > 0) {
+ functions += ",";
+ }
+ functions += function;
+ }
+ return functions;
+ }
+
+ /** @hide */
+ public static String removeFunction(String functions, String function) {
+ String[] split = functions.split(",");
+ for (int i = 0; i < split.length; i++) {
+ if (function.equals(split[i])) {
+ split[i] = null;
+ }
+ }
+ if (split.length == 1 && split[0] == null) {
+ return "none";
+ }
+ StringBuilder builder = new StringBuilder();
+ for (int i = 0; i < split.length; i++) {
+ String s = split[i];
+ if (s != null) {
+ if (builder.length() > 0) {
+ builder.append(",");
+ }
+ builder.append(s);
+ }
+ }
+ return builder.toString();
+ }
+
+ /** @hide */
+ public static boolean containsFunction(String functions, String function) {
+ int index = functions.indexOf(function);
+ if (index < 0) return false;
+ if (index > 0 && functions.charAt(index - 1) != ',') return false;
+ int charAfter = index + function.length();
+ if (charAfter < functions.length() && functions.charAt(charAfter) != ',') return false;
+ return true;
+ }
}
diff --git a/core/java/android/inputmethodservice/ExtractEditText.java b/core/java/android/inputmethodservice/ExtractEditText.java
index 8bc2876..b039fc7 100644
--- a/core/java/android/inputmethodservice/ExtractEditText.java
+++ b/core/java/android/inputmethodservice/ExtractEditText.java
@@ -29,7 +29,7 @@ import android.widget.EditText;
public class ExtractEditText extends EditText {
private InputMethodService mIME;
private int mSettingExtractedText;
-
+
public ExtractEditText(Context context) {
super(context, null);
}
@@ -45,11 +45,11 @@ public class ExtractEditText extends EditText {
public ExtractEditText(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
-
+
void setIME(InputMethodService ime) {
mIME = ime;
}
-
+
/**
* Start making changes that will not be reported to the client. That
* is, {@link #onSelectionChanged(int, int)} will not result in sending
@@ -58,7 +58,7 @@ public class ExtractEditText extends EditText {
public void startInternalChanges() {
mSettingExtractedText += 1;
}
-
+
/**
* Finish making changes that will not be reported to the client. That
* is, {@link #onSelectionChanged(int, int)} will not result in sending
@@ -67,7 +67,7 @@ public class ExtractEditText extends EditText {
public void finishInternalChanges() {
mSettingExtractedText -= 1;
}
-
+
/**
* Implement just to keep track of when we are setting text from the
* client (vs. seeing changes in ourself from the user).
@@ -80,7 +80,7 @@ public class ExtractEditText extends EditText {
mSettingExtractedText--;
}
}
-
+
/**
* Report to the underlying text editor about selection changes.
*/
@@ -89,7 +89,7 @@ public class ExtractEditText extends EditText {
mIME.onExtractedSelectionChanged(selStart, selEnd);
}
}
-
+
/**
* Redirect clicks to the IME for handling there. First allows any
* on click handler to run, though.
@@ -101,17 +101,18 @@ public class ExtractEditText extends EditText {
}
return false;
}
-
+
@Override public boolean onTextContextMenuItem(int id) {
- if (mIME != null && mIME.onExtractTextContextMenuItem(id)) {
+ // Select all shouldn't be handled by the original edit text, but by the extracted one.
+ if (id != android.R.id.selectAll && mIME != null && mIME.onExtractTextContextMenuItem(id)) {
// Mode was started on Extracted, needs to be stopped here.
- // Cut and paste will change the text, which stops selection mode.
- if (id == android.R.id.copy) stopTextActionMode();
+ // Cut will change the text, which stops selection mode.
+ if (id == android.R.id.copy || id == android.R.id.paste) stopTextActionMode();
return true;
}
return super.onTextContextMenuItem(id);
}
-
+
/**
* We are always considered to be an input method target.
*/
@@ -119,14 +120,14 @@ public class ExtractEditText extends EditText {
public boolean isInputMethodTarget() {
return true;
}
-
+
/**
* Return true if the edit text is currently showing a scroll bar.
*/
public boolean hasVerticalScrollBar() {
return computeVerticalScrollRange() > computeVerticalScrollExtent();
}
-
+
/**
* Pretend like the window this view is in always has focus, so its
* highlight and cursor will be displayed.
diff --git a/core/java/android/net/CaptivePortal.java b/core/java/android/net/CaptivePortal.java
new file mode 100644
index 0000000..ee05f28
--- /dev/null
+++ b/core/java/android/net/CaptivePortal.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed urnder 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.net;
+
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.RemoteException;
+
+/**
+ * A class allowing apps handling the {@link ConnectivityManager#ACTION_CAPTIVE_PORTAL_SIGN_IN}
+ * activity to indicate to the system different outcomes of captive portal sign in. This class is
+ * passed as an extra named {@link ConnectivityManager#EXTRA_CAPTIVE_PORTAL} with the
+ * {@code ACTION_CAPTIVE_PORTAL_SIGN_IN} activity.
+ */
+public class CaptivePortal implements Parcelable {
+ /** @hide */
+ public static final int APP_RETURN_DISMISSED = 0;
+ /** @hide */
+ public static final int APP_RETURN_UNWANTED = 1;
+ /** @hide */
+ public static final int APP_RETURN_WANTED_AS_IS = 2;
+
+ private final IBinder mBinder;
+
+ /** @hide */
+ public CaptivePortal(IBinder binder) {
+ mBinder = binder;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeStrongBinder(mBinder);
+ }
+
+ public static final Parcelable.Creator<CaptivePortal> CREATOR
+ = new Parcelable.Creator<CaptivePortal>() {
+ @Override
+ public CaptivePortal createFromParcel(Parcel in) {
+ return new CaptivePortal(in.readStrongBinder());
+ }
+
+ @Override
+ public CaptivePortal[] newArray(int size) {
+ return new CaptivePortal[size];
+ }
+ };
+
+ /**
+ * Indicate to the system that the captive portal has been
+ * dismissed. In response the framework will re-evaluate the network's
+ * connectivity and might take further action thereafter.
+ */
+ public void reportCaptivePortalDismissed() {
+ try {
+ ICaptivePortal.Stub.asInterface(mBinder).appResponse(APP_RETURN_DISMISSED);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Indicate to the system that the user does not want to pursue signing in to the
+ * captive portal and the system should continue to prefer other networks
+ * without captive portals for use as the default active data network. The
+ * system will not retest the network for a captive portal so as to avoid
+ * disturbing the user with further sign in to network notifications.
+ */
+ public void ignoreNetwork() {
+ try {
+ ICaptivePortal.Stub.asInterface(mBinder).appResponse(APP_RETURN_UNWANTED);
+ } catch (RemoteException e) {
+ }
+ }
+
+ /**
+ * Indicate to the system the user wants to use this network as is, even though
+ * the captive portal is still in place. The system will treat the network
+ * as if it did not have a captive portal when selecting the network to use
+ * as the default active data network. This may result in this network
+ * becoming the default active data network, which could disrupt network
+ * connectivity for apps because the captive portal is still in place.
+ * @hide
+ */
+ public void useNetwork() {
+ try {
+ ICaptivePortal.Stub.asInterface(mBinder).appResponse(APP_RETURN_WANTED_AS_IS);
+ } catch (RemoteException e) {
+ }
+ }
+}
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 01334c3..dc8ff8f 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -102,26 +102,26 @@ public class ConnectivityManager {
* portal, which is blocking Internet connectivity. The user was presented
* with a notification that network sign in is required,
* and the user invoked the notification's action indicating they
- * desire to sign in to the network. Apps handling this action should
+ * desire to sign in to the network. Apps handling this activity should
* facilitate signing in to the network. This action includes a
* {@link Network} typed extra called {@link #EXTRA_NETWORK} that represents
* the network presenting the captive portal; all communication with the
* captive portal must be done using this {@code Network} object.
* <p/>
- * When the app handling this action believes the user has signed in to
- * the network and the captive portal has been dismissed, the app should call
- * {@link #reportCaptivePortalDismissed} so the system can reevaluate the network.
- * If reevaluation finds the network no longer subject to a captive portal,
- * the network may become the default active data network.
- * <p/>
- * When the app handling this action believes the user explicitly wants
+ * This activity includes a {@link CaptivePortal} extra named
+ * {@link #EXTRA_CAPTIVE_PORTAL} that can be used to indicate different
+ * outcomes of the captive portal sign in to the system:
+ * <ul>
+ * <li> When the app handling this action believes the user has signed in to
+ * the network and the captive portal has been dismissed, the app should
+ * call {@link CaptivePortal#reportCaptivePortalDismissed} so the system can
+ * reevaluate the network. If reevaluation finds the network no longer
+ * subject to a captive portal, the network may become the default active
+ * data network. </li>
+ * <li> When the app handling this action believes the user explicitly wants
* to ignore the captive portal and the network, the app should call
- * {@link #ignoreNetworkWithCaptivePortal}.
- * <p/>
- * Note that this action includes a {@code String} extra named
- * {@link #EXTRA_CAPTIVE_PORTAL_TOKEN} that must
- * be passed in to {@link #reportCaptivePortalDismissed} and
- * {@link #ignoreNetworkWithCaptivePortal}.
+ * {@link CaptivePortal#ignoreNetwork}. </li>
+ * </ul>
*/
@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
public static final String ACTION_CAPTIVE_PORTAL_SIGN_IN = "android.net.conn.CAPTIVE_PORTAL";
@@ -187,16 +187,15 @@ public class ConnectivityManager {
* {@hide}
*/
public static final String EXTRA_INET_CONDITION = "inetCondition";
-
/**
- * The lookup key for a string that is sent out with
- * {@link #ACTION_CAPTIVE_PORTAL_SIGN_IN}. This string must be
- * passed in to {@link #reportCaptivePortalDismissed} and
- * {@link #ignoreNetworkWithCaptivePortal}. Retrieve it with
- * {@link android.content.Intent#getStringExtra(String)}.
+ * The lookup key for a {@link CaptivePortal} object included with the
+ * {@link #ACTION_CAPTIVE_PORTAL_SIGN_IN} intent. The {@code CaptivePortal}
+ * object can be used to either indicate to the system that the captive
+ * portal has been dismissed or that the user does not want to pursue
+ * signing in to captive portal. Retrieve it with
+ * {@link android.content.Intent#getParcelableExtra(String)}.
*/
- public static final String EXTRA_CAPTIVE_PORTAL_TOKEN = "captivePortalToken";
-
+ public static final String EXTRA_CAPTIVE_PORTAL = "android.net.extra.CAPTIVE_PORTAL";
/**
* Broadcast action to indicate the change of data activity status
* (idle or active) on a network in a recent period.
@@ -432,7 +431,8 @@ public class ConnectivityManager {
public static final int TYPE_MOBILE_IA = 14;
/**
- * Emergency PDN connection for emergency calls
+ * Emergency PDN connection for emergency services. This
+ * may include IMS and MMS in emergency situations.
* {@hide}
*/
public static final int TYPE_MOBILE_EMERGENCY = 15;
@@ -1779,82 +1779,6 @@ public class ConnectivityManager {
}
}
- /** {@hide} */
- public static final int CAPTIVE_PORTAL_APP_RETURN_DISMISSED = 0;
- /** {@hide} */
- public static final int CAPTIVE_PORTAL_APP_RETURN_UNWANTED = 1;
- /** {@hide} */
- public static final int CAPTIVE_PORTAL_APP_RETURN_WANTED_AS_IS = 2;
-
- /**
- * Called by an app handling the {@link #ACTION_CAPTIVE_PORTAL_SIGN_IN}
- * action to indicate to the system that the captive portal has been
- * dismissed. In response the framework will re-evaluate the network's
- * connectivity and might take further action thereafter.
- *
- * @param network The {@link Network} object passed via
- * {@link #EXTRA_NETWORK} with the
- * {@link #ACTION_CAPTIVE_PORTAL_SIGN_IN} action.
- * @param actionToken The {@code String} passed via
- * {@link #EXTRA_CAPTIVE_PORTAL_TOKEN} with the
- * {@code ACTION_CAPTIVE_PORTAL_SIGN_IN} action.
- */
- public void reportCaptivePortalDismissed(Network network, String actionToken) {
- try {
- mService.captivePortalAppResponse(network, CAPTIVE_PORTAL_APP_RETURN_DISMISSED,
- actionToken);
- } catch (RemoteException e) {
- }
- }
-
- /**
- * Called by an app handling the {@link #ACTION_CAPTIVE_PORTAL_SIGN_IN}
- * action to indicate that the user does not want to pursue signing in to
- * captive portal and the system should continue to prefer other networks
- * without captive portals for use as the default active data network. The
- * system will not retest the network for a captive portal so as to avoid
- * disturbing the user with further sign in to network notifications.
- *
- * @param network The {@link Network} object passed via
- * {@link #EXTRA_NETWORK} with the
- * {@link #ACTION_CAPTIVE_PORTAL_SIGN_IN} action.
- * @param actionToken The {@code String} passed via
- * {@link #EXTRA_CAPTIVE_PORTAL_TOKEN} with the
- * {@code ACTION_CAPTIVE_PORTAL_SIGN_IN} action.
- */
- public void ignoreNetworkWithCaptivePortal(Network network, String actionToken) {
- try {
- mService.captivePortalAppResponse(network, CAPTIVE_PORTAL_APP_RETURN_UNWANTED,
- actionToken);
- } catch (RemoteException e) {
- }
- }
-
- /**
- * Called by an app handling the {@link #ACTION_CAPTIVE_PORTAL_SIGN_IN}
- * action to indicate the user wants to use this network as is, even though
- * the captive portal is still in place. The system will treat the network
- * as if it did not have a captive portal when selecting the network to use
- * as the default active data network. This may result in this network
- * becoming the default active data network, which could disrupt network
- * connectivity for apps because the captive portal is still in place.
- *
- * @param network The {@link Network} object passed via
- * {@link #EXTRA_NETWORK} with the
- * {@link #ACTION_CAPTIVE_PORTAL_SIGN_IN} action.
- * @param actionToken The {@code String} passed via
- * {@link #EXTRA_CAPTIVE_PORTAL_TOKEN} with the
- * {@code ACTION_CAPTIVE_PORTAL_SIGN_IN} action.
- * @hide
- */
- public void useNetworkWithCaptivePortal(Network network, String actionToken) {
- try {
- mService.captivePortalAppResponse(network, CAPTIVE_PORTAL_APP_RETURN_WANTED_AS_IS,
- actionToken);
- } catch (RemoteException e) {
- }
- }
-
/**
* Set a network-independent global http proxy. This is not normally what you want
* for typical HTTP proxies - they are general network dependent. However if you're
diff --git a/core/java/android/net/ICaptivePortal.aidl b/core/java/android/net/ICaptivePortal.aidl
new file mode 100644
index 0000000..a013e79
--- /dev/null
+++ b/core/java/android/net/ICaptivePortal.aidl
@@ -0,0 +1,26 @@
+/**
+ * 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.net;
+
+/**
+ * Interface to inform NetworkMonitor of decisions of app handling captive portal.
+ * @hide
+ */
+interface ICaptivePortal
+{
+ oneway void appResponse(int response);
+}
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index 29557bb..46c28a6 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -96,8 +96,6 @@ interface IConnectivityManager
void reportNetworkConnectivity(in Network network, boolean hasConnectivity);
- void captivePortalAppResponse(in Network network, int response, String actionToken);
-
ProxyInfo getGlobalProxy();
void setGlobalProxy(in ProxyInfo p);
@@ -114,7 +112,7 @@ interface IConnectivityManager
void startLegacyVpn(in VpnProfile profile);
- LegacyVpnInfo getLegacyVpnInfo();
+ LegacyVpnInfo getLegacyVpnInfo(int userId);
VpnInfo[] getAllVpnInfo();
diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java
index 658051c..514d24a 100644
--- a/core/java/android/net/NetworkCapabilities.java
+++ b/core/java/android/net/NetworkCapabilities.java
@@ -133,7 +133,8 @@ public final class NetworkCapabilities implements Parcelable {
/**
* Indicates this is a network that has the ability to reach a carrier's
- * Emergency IMS servers, used for network signaling during emergency calls.
+ * Emergency IMS servers or other services, used for network signaling
+ * during emergency calls.
*/
public static final int NET_CAPABILITY_EIMS = 10;
diff --git a/core/java/android/os/PooledStringReader.java b/core/java/android/os/PooledStringReader.java
index 7795957..6fc71c7 100644
--- a/core/java/android/os/PooledStringReader.java
+++ b/core/java/android/os/PooledStringReader.java
@@ -36,6 +36,10 @@ public class PooledStringReader {
mPool = new String[size];
}
+ public int getStringCount() {
+ return mPool.length;
+ }
+
public String readString() {
int idx = mIn.readInt();
if (idx >= 0) {
diff --git a/core/java/android/os/PooledStringWriter.java b/core/java/android/os/PooledStringWriter.java
index eac297d..ee592d9 100644
--- a/core/java/android/os/PooledStringWriter.java
+++ b/core/java/android/os/PooledStringWriter.java
@@ -67,6 +67,10 @@ public class PooledStringWriter {
}
}
+ public int getStringCount() {
+ return mPool.size();
+ }
+
public void finish() {
final int pos = mOut.dataPosition();
mOut.setDataPosition(mStart);
diff --git a/core/java/android/os/storage/IMountService.java b/core/java/android/os/storage/IMountService.java
index 84a879c..c3b098b 100644
--- a/core/java/android/os/storage/IMountService.java
+++ b/core/java/android/os/storage/IMountService.java
@@ -758,13 +758,15 @@ public interface IMountService extends IInterface {
return _result;
}
- public StorageVolume[] getVolumeList(int userId) throws RemoteException {
+ public StorageVolume[] getVolumeList(int uid, String packageName)
+ throws RemoteException {
Parcel _data = Parcel.obtain();
Parcel _reply = Parcel.obtain();
StorageVolume[] _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
- _data.writeInt(userId);
+ _data.writeInt(uid);
+ _data.writeString(packageName);
mRemote.transact(Stub.TRANSACTION_getVolumeList, _data, _reply, 0);
_reply.readException();
_result = _reply.createTypedArray(StorageVolume.CREATOR);
@@ -1177,21 +1179,6 @@ public interface IMountService extends IInterface {
_data.recycle();
}
}
-
- @Override
- public void remountUid(int uid) throws RemoteException {
- Parcel _data = Parcel.obtain();
- Parcel _reply = Parcel.obtain();
- try {
- _data.writeInterfaceToken(DESCRIPTOR);
- _data.writeInt(uid);
- mRemote.transact(Stub.TRANSACTION_remountUid, _data, _reply, 0);
- _reply.readException();
- } finally {
- _reply.recycle();
- _data.recycle();
- }
- }
}
private static final String DESCRIPTOR = "IMountService";
@@ -1307,8 +1294,6 @@ public interface IMountService extends IInterface {
static final int TRANSACTION_benchmark = IBinder.FIRST_CALL_TRANSACTION + 59;
static final int TRANSACTION_setDebugFlags = IBinder.FIRST_CALL_TRANSACTION + 60;
- static final int TRANSACTION_remountUid = IBinder.FIRST_CALL_TRANSACTION + 61;
-
/**
* Cast an IBinder object into an IMountService interface, generating a
* proxy if needed.
@@ -1622,8 +1607,9 @@ public interface IMountService extends IInterface {
}
case TRANSACTION_getVolumeList: {
data.enforceInterface(DESCRIPTOR);
- int userId = data.readInt();
- StorageVolume[] result = getVolumeList(userId);
+ int uid = data.readInt();
+ String packageName = data.readString();
+ StorageVolume[] result = getVolumeList(uid, packageName);
reply.writeNoException();
reply.writeTypedArray(result, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
return true;
@@ -1862,13 +1848,6 @@ public interface IMountService extends IInterface {
reply.writeNoException();
return true;
}
- case TRANSACTION_remountUid: {
- data.enforceInterface(DESCRIPTOR);
- int uid = data.readInt();
- remountUid(uid);
- reply.writeNoException();
- return true;
- }
}
return super.onTransact(code, data, reply, flags);
}
@@ -2080,11 +2059,11 @@ public interface IMountService extends IInterface {
/**
* Returns list of all mountable volumes.
*/
- public StorageVolume[] getVolumeList(int userId) throws RemoteException;
+ public StorageVolume[] getVolumeList(int uid, String packageName) throws RemoteException;
/**
* Gets the path on the filesystem for the ASEC container itself.
- *
+ *
* @param cid ASEC container ID
* @return path to filesystem or {@code null} if it's not found
* @throws RemoteException
@@ -2178,6 +2157,4 @@ public interface IMountService extends IInterface {
public String getPrimaryStorageUuid() throws RemoteException;
public void setPrimaryStorageUuid(String volumeUuid, IPackageMoveObserver callback)
throws RemoteException;
-
- public void remountUid(int uid) throws RemoteException;
}
diff --git a/core/java/android/os/storage/MountServiceInternal.java b/core/java/android/os/storage/MountServiceInternal.java
new file mode 100644
index 0000000..17aaef9
--- /dev/null
+++ b/core/java/android/os/storage/MountServiceInternal.java
@@ -0,0 +1,82 @@
+/*
+ * 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.os.storage;
+
+/**
+ * Mount service local interface.
+ *
+ * @hide Only for use within the system server.
+ */
+public abstract class MountServiceInternal {
+
+ /**
+ * Policy that influences how external storage is mounted and reported.
+ */
+ public interface ExternalStorageMountPolicy {
+ /**
+ * Gets the external storage mount mode for the given uid.
+ *
+ * @param uid The UID for which to determine mount mode.
+ * @param packageName The package in the UID for making the call.
+ * @return The mount mode.
+ *
+ * @see com.android.internal.os.Zygote#MOUNT_EXTERNAL_NONE
+ * @see com.android.internal.os.Zygote#MOUNT_EXTERNAL_DEFAULT
+ * @see com.android.internal.os.Zygote#MOUNT_EXTERNAL_READ
+ * @see com.android.internal.os.Zygote#MOUNT_EXTERNAL_WRITE
+ */
+ public int getMountMode(int uid, String packageName);
+
+ /**
+ * Gets whether external storage should be reported to the given UID.
+ *
+ * @param uid The UID for which to determine whether it has external storage.
+ * @param packageName The package in the UID for making the call.
+ * @return Weather to report external storage.
+ * @return True to report the state of external storage, false to
+ * report it as unmounted.
+ */
+ public boolean hasExternalStorage(int uid, String packageName);
+ }
+
+ /**
+ * Adds a policy for determining how external storage is mounted and reported.
+ * The mount mode is the most conservative result from querying all registered
+ * policies. Similarly, the reported state is the most conservative result from
+ * querying all registered policies.
+ *
+ * @param policy The policy to add.
+ */
+ public abstract void addExternalStoragePolicy(ExternalStorageMountPolicy policy);
+
+ /**
+ * Notify the mount service that the mount policy for a UID changed.
+ * @param uid The UID for which policy changed.
+ * @param packageName The package in the UID for making the call.
+ */
+ public abstract void onExternalStoragePolicyChanged(int uid, String packageName);
+
+ /**
+ * Gets the mount mode to use for a given UID as determined by consultin all
+ * policies.
+ *
+ * @param uid The UID for which to get mount mode.
+ * @param packageName The package in the UID for making the call.
+ * @return The mount mode.
+ */
+ public abstract int getExternalStorageMountMode(int uid, String packageName);
+}
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index f03e04e..320aa2c 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -20,6 +20,7 @@ import static android.net.TrafficStats.MB_IN_BYTES;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.ActivityThread;
import android.content.ContentResolver;
import android.content.Context;
import android.content.pm.IPackageMoveObserver;
@@ -857,7 +858,9 @@ public class StorageManager {
final IMountService mountService = IMountService.Stub.asInterface(
ServiceManager.getService("mount"));
try {
- return mountService.getVolumeList(userId);
+ final String packageName = ActivityThread.currentOpPackageName();
+ final int uid = ActivityThread.getPackageManager().getPackageUid(packageName, userId);
+ return mountService.getVolumeList(uid, packageName);
} catch (RemoteException e) {
throw e.rethrowAsRuntimeException();
}
@@ -894,15 +897,6 @@ public class StorageManager {
}
/** {@hide} */
- public void remountUid(int uid) {
- try {
- mMountService.remountUid(uid);
- } catch (RemoteException e) {
- throw e.rethrowAsRuntimeException();
- }
- }
-
- /** {@hide} */
private static final int DEFAULT_THRESHOLD_PERCENTAGE = 10;
private static final long DEFAULT_THRESHOLD_MAX_BYTES = 500 * MB_IN_BYTES;
private static final long DEFAULT_FULL_THRESHOLD_BYTES = MB_IN_BYTES;
@@ -956,7 +950,7 @@ public class StorageManager {
|| vol.getType() == VolumeInfo.TYPE_PUBLIC) && vol.isMountedReadable()) {
final File internalPath = FileUtils.rewriteAfterRename(vol.getPath(),
vol.getInternalPath(), path);
- if (internalPath != null) {
+ if (internalPath != null && internalPath.exists()) {
return internalPath;
}
}
diff --git a/core/java/android/os/storage/VolumeInfo.java b/core/java/android/os/storage/VolumeInfo.java
index 32f7bc9..8d603a1 100644
--- a/core/java/android/os/storage/VolumeInfo.java
+++ b/core/java/android/os/storage/VolumeInfo.java
@@ -298,14 +298,14 @@ public class VolumeInfo implements Parcelable {
}
}
- public StorageVolume buildStorageVolume(Context context, int userId) {
+ public StorageVolume buildStorageVolume(Context context, int userId, boolean reportUnmounted) {
final StorageManager storage = context.getSystemService(StorageManager.class);
final boolean removable;
final boolean emulated;
final boolean allowMassStorage = false;
- final String envState = getEnvironmentForState(state);
-
+ final String envState = reportUnmounted
+ ? Environment.MEDIA_UNMOUNTED : getEnvironmentForState(state);
File userPath = getPathForUser(userId);
if (userPath == null) {
userPath = new File("/dev/null");
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 4e13758..fff355b 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -574,7 +574,22 @@ public final class Settings {
*/
@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
public static final String ACTION_MANAGE_OVERLAY_PERMISSION =
- "android.settings.MANAGE_OVERLAY_PERMISSION";
+ "android.settings.action.MANAGE_OVERLAY_PERMISSION";
+
+ /**
+ * Activity Action: Show settings to toggle apps' capablity to
+ * to read/write system settings.
+ * <p>
+ * In some cases, a matching Activity may not exist, so ensure you
+ * safeguard against this.
+ * <p>
+ * Input: Nothing.
+ * <p>
+ * Output: Nothing.
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_MANAGE_WRITE_SETTINGS =
+ "android.settings.action.MANAGE_WRITE_SETTINGS";
/**
* Activity Action: Show screen of details about a particular application.
diff --git a/core/java/android/service/voice/VoiceInteractionSession.java b/core/java/android/service/voice/VoiceInteractionSession.java
index a7e0e08..e408b36 100644
--- a/core/java/android/service/voice/VoiceInteractionSession.java
+++ b/core/java/android/service/voice/VoiceInteractionSession.java
@@ -209,16 +209,30 @@ public class VoiceInteractionSession implements KeyEvent.Callback, ComponentCall
}
@Override
- public void handleAssist(Bundle data, AssistStructure structure,
- AssistContent content) {
+ public void handleAssist(final Bundle data, final AssistStructure structure,
+ final AssistContent content) {
// We want to pre-warm the AssistStructure before handing it off to the main
- // thread. There is a strong argument to be made that it should be handed
- // through as a separate param rather than part of the assistBundle.
- if (structure != null) {
- structure.ensureData();
- }
- mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageOOO(MSG_HANDLE_ASSIST,
- data, structure, content));
+ // thread. We also want to do this on a separate thread, so that if the app
+ // is for some reason slow (due to slow filling in of async children in the
+ // structure), we don't block other incoming IPCs (such as the screenshot) to
+ // us (since we are a oneway interface, they get serialized). (Okay?)
+ Thread retriever = new Thread("AssistStructure retriever") {
+ @Override
+ public void run() {
+ Throwable failure = null;
+ if (structure != null) {
+ try {
+ structure.ensureData();
+ } catch (Throwable e) {
+ Log.w(TAG, "Failure retrieving AssistStructure", e);
+ failure = e;
+ }
+ }
+ mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageOOOO(MSG_HANDLE_ASSIST,
+ data, failure == null ? structure : null, failure, content));
+ }
+ };
+ retriever.start();
}
@Override
@@ -689,8 +703,8 @@ public class VoiceInteractionSession implements KeyEvent.Callback, ComponentCall
args = (SomeArgs)msg.obj;
if (DEBUG) Log.d(TAG, "onHandleAssist: data=" + args.arg1
+ " structure=" + args.arg2 + " content=" + args.arg3);
- onHandleAssist((Bundle) args.arg1, (AssistStructure) args.arg2,
- (AssistContent) args.arg3);
+ doOnHandleAssist((Bundle) args.arg1, (AssistStructure) args.arg2,
+ (Throwable) args.arg3, (AssistContent) args.arg4);
break;
case MSG_HANDLE_SCREENSHOT:
if (DEBUG) Log.d(TAG, "onHandleScreenshot: " + msg.obj);
@@ -1111,9 +1125,45 @@ public class VoiceInteractionSession implements KeyEvent.Callback, ComponentCall
mContentFrame.requestApplyInsets();
}
+ void doOnHandleAssist(Bundle data, AssistStructure structure, Throwable failure,
+ AssistContent content) {
+ if (failure != null) {
+ onAssistStructureFailure(failure);
+ }
+ onHandleAssist(data, structure, content);
+ }
+
+ /**
+ * Called when there has been a failure transferring the {@link AssistStructure} to
+ * the assistant. This may happen, for example, if the data is too large and results
+ * in an out of memory exception, or the client has provided corrupt data. This will
+ * be called immediately before {@link #onHandleAssist} and the AssistStructure supplied
+ * there afterwards will be null.
+ *
+ * @param failure The failure exception that was thrown when building the
+ * {@link AssistStructure}.
+ */
+ public void onAssistStructureFailure(Throwable failure) {
+ }
+
+ /**
+ * Called to receive data from the application that the user was currently viewing when
+ * an assist session is started.
+ *
+ * @param data Arbitrary data supplied by the app through
+ * {@link android.app.Activity#onProvideAssistData Activity.onProvideAssistData}.
+ * @param structure If available, the structure definition of all windows currently
+ * displayed by the app; if structure has been turned off by the user, will be null.
+ * @param content Additional content data supplied by the app through
+ * {@link android.app.Activity#onProvideAssistContent Activity.onProvideAssistContent}.
+ */
public void onHandleAssist(Bundle data, AssistStructure structure, AssistContent content) {
}
+ /**
+ * Called to receive a screenshot of what the user was currently viewing when an assist
+ * session is started. Will be null if screenshots are disabled by the user.
+ */
public void onHandleScreenshot(Bitmap screenshot) {
}
diff --git a/core/java/android/text/BoringLayout.java b/core/java/android/text/BoringLayout.java
index e78cf8f..aa8b71c 100644
--- a/core/java/android/text/BoringLayout.java
+++ b/core/java/android/text/BoringLayout.java
@@ -269,7 +269,15 @@ public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback
for (int a = 0; a < n; a++) {
char c = temp[a];
- if (c == '\n' || c == '\t' || c >= FIRST_RIGHT_TO_LEFT) {
+ if (c == '\n' || c == '\t' ||
+ (c >= 0x0590 && c <= 0x08FF) || // RTL scripts
+ c == 0x200F || // Bidi format character
+ (c >= 0x202A && c <= 0x202E) || // Bidi format characters
+ (c >= 0x2066 && c <= 0x2069) || // Bidi format characters
+ (c >= 0xD800 && c <= 0xDFFF) || // surrogate pairs
+ (c >= 0xFB1D && c <= 0xFDFF) || // Hebrew and Arabic presentation forms
+ (c >= 0xFE70 && c <= 0xFEFE) // Arabic presentation forms
+ ) {
boring = false;
break outer;
}
diff --git a/core/java/android/text/Html.java b/core/java/android/text/Html.java
index a55a08c..dc1d6f6 100644
--- a/core/java/android/text/Html.java
+++ b/core/java/android/text/Html.java
@@ -137,7 +137,12 @@ public class Html {
}
/**
- * Returns an HTML representation of the provided Spanned text.
+ * Returns an HTML representation of the provided Spanned text. A best effort is
+ * made to add HTML tags corresponding to spans. Also note that HTML metacharacters
+ * (such as "&lt;" and "&amp;") within the input text are escaped.
+ *
+ * @param text input text to convert
+ * @return string containing input converted to HTML
*/
public static String toHtml(Spanned text) {
StringBuilder out = new StringBuilder();
diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java
index d822138..fa347b9 100644
--- a/core/java/android/text/Layout.java
+++ b/core/java/android/text/Layout.java
@@ -206,7 +206,6 @@ public abstract class Layout {
mText = text;
mPaint = paint;
- mWorkPaint = new TextPaint();
mWidth = width;
mAlignment = align;
mSpacingMult = spacingMult;
@@ -1993,7 +1992,6 @@ public abstract class Layout {
private CharSequence mText;
private TextPaint mPaint;
- /* package */ TextPaint mWorkPaint;
private int mWidth;
private Alignment mAlignment = Alignment.ALIGN_NORMAL;
private float mSpacingMult;
diff --git a/core/java/android/text/SpannableStringInternal.java b/core/java/android/text/SpannableStringInternal.java
index d114d32..5c5deb4 100644
--- a/core/java/android/text/SpannableStringInternal.java
+++ b/core/java/android/text/SpannableStringInternal.java
@@ -214,10 +214,6 @@ import java.lang.reflect.Array;
Object ret1 = null;
for (int i = 0; i < spanCount; i++) {
- if (kind != null && !kind.isInstance(spans[i])) {
- continue;
- }
-
int spanStart = data[i * COLUMNS + START];
int spanEnd = data[i * COLUMNS + END];
@@ -237,6 +233,11 @@ import java.lang.reflect.Array;
}
}
+ // verify span class as late as possible, since it is expensive
+ if (kind != null && !kind.isInstance(spans[i])) {
+ continue;
+ }
+
if (count == 0) {
ret1 = spans[i];
count++;
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index cdff395..efc9e1a 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -744,7 +744,8 @@ public class StaticLayout extends Layout {
&& (ellipsize == TextUtils.TruncateAt.END
|| (mMaximumVisibleLineCount == 1
&& ellipsize != TextUtils.TruncateAt.MARQUEE));
- if (remainingLineCount < breakCount && ellipsisMayBeApplied) {
+ if (remainingLineCount > 0 && remainingLineCount < breakCount &&
+ ellipsisMayBeApplied) {
// Treat the last line and overflowed lines as a single line.
breaks[remainingLineCount - 1] = breaks[breakCount - 1];
// Calculate width and flag.
diff --git a/core/java/android/util/LocalLog.java b/core/java/android/util/LocalLog.java
index 4862f01..39f66a5 100644
--- a/core/java/android/util/LocalLog.java
+++ b/core/java/android/util/LocalLog.java
@@ -55,6 +55,12 @@ public final class LocalLog {
}
}
+ public synchronized void reverseDump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ for (int i = mLog.size() - 1; i >= 0; i--) {
+ pw.println(mLog.get(i));
+ }
+ }
+
public static class ReadOnlyLocalLog {
private final LocalLog mLog;
ReadOnlyLocalLog(LocalLog log) {
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index 64210ea..be564f8 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -342,6 +342,14 @@ public interface WindowManagerPolicy {
boolean isGoneForLayoutLw();
/**
+ * Returns true if the window has a surface that it has drawn a
+ * complete UI in to. Note that this is different from {@link #hasDrawnLw()}
+ * in that it also returns true if the window is READY_TO_SHOW, but was not yet
+ * promoted to HAS_DRAWN.
+ */
+ boolean isDrawnLw();
+
+ /**
* Returns true if this window has been shown on screen at some time in
* the past. Must be called with the window manager lock held.
*/
diff --git a/core/java/android/widget/AppSecurityPermissions.java b/core/java/android/widget/AppSecurityPermissions.java
index c54b28d..e3ce6f2 100644
--- a/core/java/android/widget/AppSecurityPermissions.java
+++ b/core/java/android/widget/AppSecurityPermissions.java
@@ -61,8 +61,6 @@ import java.util.Set;
*/
public class AppSecurityPermissions {
- public static final int WHICH_PERSONAL = 1<<0;
- public static final int WHICH_DEVICE = 1<<1;
public static final int WHICH_NEW = 1<<2;
public static final int WHICH_ALL = 0xffff;
@@ -75,7 +73,8 @@ public class AppSecurityPermissions {
= new HashMap<String, MyPermissionGroupInfo>();
private final List<MyPermissionGroupInfo> mPermGroupsList
= new ArrayList<MyPermissionGroupInfo>();
- private final PermissionGroupInfoComparator mPermGroupComparator = new PermissionGroupInfoComparator();
+ private final PermissionGroupInfoComparator mPermGroupComparator =
+ new PermissionGroupInfoComparator();
private final PermissionInfoComparator mPermComparator = new PermissionInfoComparator();
private final List<MyPermissionInfo> mPermsList = new ArrayList<MyPermissionInfo>();
private final CharSequence mNewPermPrefix;
@@ -85,8 +84,6 @@ public class AppSecurityPermissions {
CharSequence mLabel;
final ArrayList<MyPermissionInfo> mNewPermissions = new ArrayList<MyPermissionInfo>();
- final ArrayList<MyPermissionInfo> mPersonalPermissions = new ArrayList<MyPermissionInfo>();
- final ArrayList<MyPermissionInfo> mDevicePermissions = new ArrayList<MyPermissionInfo>();
final ArrayList<MyPermissionInfo> mAllPermissions = new ArrayList<MyPermissionInfo>();
MyPermissionGroupInfo(PermissionInfo perm) {
@@ -98,18 +95,12 @@ public class AppSecurityPermissions {
super(info);
}
- public Drawable loadGroupIcon(PackageManager pm) {
+ public Drawable loadGroupIcon(Context context, PackageManager pm) {
if (icon != 0) {
return loadUnbadgedIcon(pm);
} else {
- ApplicationInfo appInfo;
- try {
- appInfo = pm.getApplicationInfo(packageName, 0);
- return appInfo.loadUnbadgedIcon(pm);
- } catch (NameNotFoundException e) {
- }
+ return context.getDrawable(R.drawable.ic_perm_device_info);
}
- return null;
}
}
@@ -163,7 +154,7 @@ public class AppSecurityPermissions {
PackageManager pm = getContext().getPackageManager();
Drawable icon = null;
if (first) {
- icon = grp.loadGroupIcon(pm);
+ icon = grp.loadGroupIcon(getContext(), pm);
}
CharSequence label = perm.mLabel;
if (perm.mNew && newPermPrefix != null) {
@@ -213,7 +204,7 @@ public class AppSecurityPermissions {
builder.setMessage(sbuilder.toString());
}
builder.setCancelable(true);
- builder.setIcon(mGroup.loadGroupIcon(pm));
+ builder.setIcon(mGroup.loadGroupIcon(getContext(), pm));
addRevokeUIIfNecessary(builder);
mDialog = builder.show();
mDialog.setCanceledOnTouchOutside(true);
@@ -358,13 +349,6 @@ public class AppSecurityPermissions {
}
for (int i=0; i<strList.length; i++) {
String permName = strList[i];
- // If we are only looking at an existing app, then we only
- // care about permissions that have actually been granted to it.
- if (installedPkgInfo != null && info != installedPkgInfo) {
- if ((flagsList[i]&PackageInfo.REQUESTED_PERMISSION_GRANTED) == 0) {
- continue;
- }
- }
try {
PermissionInfo tmpPermInfo = mPm.getPermissionInfo(permName, 0);
if (tmpPermInfo == null) {
@@ -437,10 +421,6 @@ public class AppSecurityPermissions {
private List<MyPermissionInfo> getPermissionList(MyPermissionGroupInfo grp, int which) {
if (which == WHICH_NEW) {
return grp.mNewPermissions;
- } else if (which == WHICH_PERSONAL) {
- return grp.mPersonalPermissions;
- } else if (which == WHICH_DEVICE) {
- return grp.mDevicePermissions;
} else {
return grp.mAllPermissions;
}
@@ -583,15 +563,8 @@ public class AppSecurityPermissions {
private static class PermissionGroupInfoComparator implements Comparator<MyPermissionGroupInfo> {
private final Collator sCollator = Collator.getInstance();
- PermissionGroupInfoComparator() {
- }
+ @Override
public final int compare(MyPermissionGroupInfo a, MyPermissionGroupInfo b) {
- if (((a.flags^b.flags)&PermissionGroupInfo.FLAG_PERSONAL_INFO) != 0) {
- return ((a.flags&PermissionGroupInfo.FLAG_PERSONAL_INFO) != 0) ? -1 : 1;
- }
- if (a.priority != b.priority) {
- return a.priority > b.priority ? -1 : 1;
- }
return sCollator.compare(a.mLabel, b.mLabel);
}
}
@@ -634,11 +607,6 @@ public class AppSecurityPermissions {
if (pInfo.mNew) {
addPermToList(group.mNewPermissions, pInfo);
}
- if ((group.flags&PermissionGroupInfo.FLAG_PERSONAL_INFO) != 0) {
- addPermToList(group.mPersonalPermissions, pInfo);
- } else {
- addPermToList(group.mDevicePermissions, pInfo);
- }
}
}
}
@@ -658,12 +626,5 @@ public class AppSecurityPermissions {
mPermGroupsList.add(pgrp);
}
Collections.sort(mPermGroupsList, mPermGroupComparator);
- if (localLOGV) {
- for (MyPermissionGroupInfo grp : mPermGroupsList) {
- Log.i(TAG, "Group " + grp.name + " personal="
- + ((grp.flags&PermissionGroupInfo.FLAG_PERSONAL_INFO) != 0)
- + " priority=" + grp.priority);
- }
- }
}
}
diff --git a/core/java/android/widget/Editor.java b/core/java/android/widget/Editor.java
index 96e033a..d897f49 100644
--- a/core/java/android/widget/Editor.java
+++ b/core/java/android/widget/Editor.java
@@ -16,6 +16,12 @@
package android.widget;
+import java.text.BreakIterator;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+
import android.R;
import android.annotation.Nullable;
import android.app.PendingIntent;
@@ -106,12 +112,6 @@ import com.android.internal.util.GrowingArrayUtils;
import com.android.internal.util.Preconditions;
import com.android.internal.widget.EditableInputConnection;
-import java.text.BreakIterator;
-import java.util.Arrays;
-import java.util.Comparator;
-import java.util.HashMap;
-import java.util.List;
-
/**
* Helper class used by TextView to handle editable text views.
@@ -127,6 +127,7 @@ public class Editor {
private static int DRAG_SHADOW_MAX_TEXT_LENGTH = 20;
private static final float LINE_SLOP_MULTIPLIER_FOR_HANDLEVIEWS = 0.5f;
private static final int UNSET_X_VALUE = -1;
+ private static final int UNSET_LINE = -1;
// Tag used when the Editor maintains its own separate UndoManager.
private static final String UNDO_OWNER_TAG = "Editor";
@@ -3510,7 +3511,11 @@ public class Editor {
// Minimum touch target size for handles
private int mMinSize;
// Indicates the line of text that the handle is on.
- protected int mPrevLine = -1;
+ protected int mPrevLine = UNSET_LINE;
+ // Indicates the line of text that the user was touching. This can differ from mPrevLine
+ // when selecting text when the handles jump to the end / start of words which may be on
+ // a different line.
+ protected int mPreviousLineTouched = UNSET_LINE;
public HandleView(Drawable drawableLtr, Drawable drawableRtl) {
super(mTextView.getContext());
@@ -3801,6 +3806,7 @@ public class Editor {
mLastParentX = positionListener.getPositionX();
mLastParentY = positionListener.getPositionY();
mIsDragging = true;
+ mPreviousLineTouched = UNSET_LINE;
break;
}
@@ -4015,8 +4021,12 @@ public class Editor {
Layout layout = mTextView.getLayout();
int offset;
if (layout != null) {
- int currLine = getCurrentLineAdjustedForSlop(layout, mPrevLine, y);
+ if (mPreviousLineTouched == UNSET_LINE) {
+ mPreviousLineTouched = mTextView.getLineAtCoordinate(y);
+ }
+ int currLine = getCurrentLineAdjustedForSlop(layout, mPreviousLineTouched, y);
offset = mTextView.getOffsetAtCoordinate(currLine, x);
+ mPreviousLineTouched = currLine;
} else {
offset = mTextView.getOffsetForPosition(x, y);
}
@@ -4092,9 +4102,13 @@ public class Editor {
return;
}
+ if (mPreviousLineTouched == UNSET_LINE) {
+ mPreviousLineTouched = mTextView.getLineAtCoordinate(y);
+ }
+
boolean positionCursor = false;
final int selectionEnd = mTextView.getSelectionEnd();
- int currLine = getCurrentLineAdjustedForSlop(layout, mPrevLine, y);
+ int currLine = getCurrentLineAdjustedForSlop(layout, mPreviousLineTouched, y);
int initialOffset = mTextView.getOffsetAtCoordinate(currLine, x);
if (initialOffset >= selectionEnd) {
@@ -4138,9 +4152,9 @@ public class Editor {
} else {
final float xDiff = x - mPrevX;
if (atRtl) {
- isExpanding = xDiff > 0 || currLine > mPrevLine;
+ isExpanding = xDiff > 0 || currLine > mPreviousLineTouched;
} else {
- isExpanding = xDiff < 0 || currLine < mPrevLine;
+ isExpanding = xDiff < 0 || currLine < mPreviousLineTouched;
}
}
@@ -4204,6 +4218,7 @@ public class Editor {
offset = getNextCursorOffset(selectionEnd, false);
mTouchWordDelta = 0.0f;
}
+ mPreviousLineTouched = currLine;
positionAtCursorOffset(offset, false);
}
mPrevX = x;
@@ -4218,8 +4233,9 @@ public class Editor {
@Override
public boolean onTouchEvent(MotionEvent event) {
boolean superResult = super.onTouchEvent(event);
- if (event.getActionMasked() == MotionEvent.ACTION_UP) {
- // Reset the touch word offset when the user has lifted their finger.
+ if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
+ // Reset the touch word offset and x value when the user
+ // re-engages the handle.
mTouchWordDelta = 0.0f;
mPrevX = UNSET_X_VALUE;
}
@@ -4280,9 +4296,13 @@ public class Editor {
return;
}
+ if (mPreviousLineTouched == UNSET_LINE) {
+ mPreviousLineTouched = mTextView.getLineAtCoordinate(y);
+ }
+
boolean positionCursor = false;
final int selectionStart = mTextView.getSelectionStart();
- int currLine = getCurrentLineAdjustedForSlop(layout, mPrevLine, y);
+ int currLine = getCurrentLineAdjustedForSlop(layout, mPreviousLineTouched, y);
int initialOffset = mTextView.getOffsetAtCoordinate(currLine, x);
if (initialOffset <= selectionStart) {
@@ -4326,9 +4346,9 @@ public class Editor {
} else {
final float xDiff = x - mPrevX;
if (atRtl) {
- isExpanding = xDiff < 0 || currLine < mPrevLine;
+ isExpanding = xDiff < 0 || currLine < mPreviousLineTouched;
} else {
- isExpanding = xDiff > 0 || currLine > mPrevLine;
+ isExpanding = xDiff > 0 || currLine > mPreviousLineTouched;
}
}
@@ -4392,6 +4412,7 @@ public class Editor {
offset = getNextCursorOffset(selectionStart, true);
mTouchWordDelta = 0.0f;
}
+ mPreviousLineTouched = currLine;
positionAtCursorOffset(offset, false);
}
mPrevX = x;
@@ -4406,8 +4427,9 @@ public class Editor {
@Override
public boolean onTouchEvent(MotionEvent event) {
boolean superResult = super.onTouchEvent(event);
- if (event.getActionMasked() == MotionEvent.ACTION_UP) {
- // Reset the touch word offset when the user has lifted their finger.
+ if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
+ // Reset the touch word offset and x value when the user
+ // re-engages the handle.
mTouchWordDelta = 0.0f;
mPrevX = UNSET_X_VALUE;
}
@@ -4416,10 +4438,16 @@ public class Editor {
}
private int getCurrentLineAdjustedForSlop(Layout layout, int prevLine, float y) {
+ final int trueLine = mTextView.getLineAtCoordinate(y);
if (layout == null || prevLine > layout.getLineCount()
|| layout.getLineCount() <= 0 || prevLine < 0) {
// Invalid parameters, just return whatever line is at y.
- return mTextView.getLineAtCoordinate(y);
+ return trueLine;
+ }
+
+ if (Math.abs(trueLine - prevLine) >= 2) {
+ // Only stick to lines if we're within a line of the previous selection.
+ return trueLine;
}
final float verticalOffset = mTextView.viewportToContentVerticalOffset();
diff --git a/core/java/android/widget/ImageView.java b/core/java/android/widget/ImageView.java
index a1582f2..e13b96f 100644
--- a/core/java/android/widget/ImageView.java
+++ b/core/java/android/widget/ImageView.java
@@ -91,6 +91,7 @@ public class ImageView extends View {
private boolean mColorMod = false;
private Drawable mDrawable = null;
+ private ImageViewBitmapDrawable mRecycleableBitmapDrawable = null;
private ColorStateList mDrawableTintList = null;
private PorterDuff.Mode mDrawableTintMode = null;
private boolean mHasDrawableTint = false;
@@ -570,6 +571,17 @@ public class ImageView extends View {
}
}
+ private static class ImageViewBitmapDrawable extends BitmapDrawable {
+ public ImageViewBitmapDrawable(Resources res, Bitmap bitmap) {
+ super(res, bitmap);
+ }
+
+ @Override
+ public void setBitmap(Bitmap bitmap) {
+ super.setBitmap(bitmap);
+ }
+ };
+
/**
* Sets a Bitmap as the content of this ImageView.
*
@@ -577,9 +589,16 @@ public class ImageView extends View {
*/
@android.view.RemotableViewMethod
public void setImageBitmap(Bitmap bm) {
- // if this is used frequently, may handle bitmaps explicitly
- // to reduce the intermediate drawable object
- setImageDrawable(new BitmapDrawable(mContext.getResources(), bm));
+ // Hacky fix to force setImageDrawable to do a full setImageDrawable
+ // instead of doing an object reference comparison
+ mDrawable = null;
+ if (mRecycleableBitmapDrawable == null) {
+ mRecycleableBitmapDrawable = new ImageViewBitmapDrawable(
+ mContext.getResources(), bm);
+ } else {
+ mRecycleableBitmapDrawable.setBitmap(bm);
+ }
+ setImageDrawable(mRecycleableBitmapDrawable);
}
public void setImageState(int[] state, boolean merge) {
@@ -848,6 +867,10 @@ public class ImageView extends View {
}
private void updateDrawable(Drawable d) {
+ if (d != mRecycleableBitmapDrawable && mRecycleableBitmapDrawable != null) {
+ mRecycleableBitmapDrawable.setBitmap(null);
+ }
+
if (mDrawable != null) {
mDrawable.setCallback(null);
unscheduleDrawable(mDrawable);
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 42ac599..131ba46 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -6347,17 +6347,28 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
if (text.text != null) {
if (content == null) {
setText(text.text, TextView.BufferType.EDITABLE);
- } else if (text.partialStartOffset < 0) {
- removeParcelableSpans(content, 0, content.length());
- content.replace(0, content.length(), text.text);
} else {
- final int N = content.length();
- int start = text.partialStartOffset;
- if (start > N) start = N;
- int end = text.partialEndOffset;
- if (end > N) end = N;
+ int start = 0;
+ int end = content.length();
+
+ if (text.partialStartOffset >= 0) {
+ final int N = content.length();
+ start = text.partialStartOffset;
+ if (start > N) start = N;
+ end = text.partialEndOffset;
+ if (end > N) end = N;
+ }
+
removeParcelableSpans(content, start, end);
- content.replace(start, end, text.text);
+ if (TextUtils.equals(content.subSequence(start, end), text.text)) {
+ if (text.text instanceof Spanned) {
+ // OK to copy spans only.
+ TextUtils.copySpansFrom((Spanned) text.text, start, end,
+ Object.class, content, start);
+ }
+ } else {
+ content.replace(start, end, text.text);
+ }
}
}