diff options
Diffstat (limited to 'core/java/android')
53 files changed, 1329 insertions, 461 deletions
diff --git a/core/java/android/accounts/AccountAuthenticatorCache.java b/core/java/android/accounts/AccountAuthenticatorCache.java index d2b3bc7..5ee1f60 100644 --- a/core/java/android/accounts/AccountAuthenticatorCache.java +++ b/core/java/android/accounts/AccountAuthenticatorCache.java @@ -18,17 +18,22 @@ package android.accounts; import android.content.pm.PackageManager; import android.content.pm.RegisteredServicesCache; +import android.content.pm.ResolveInfo; import android.content.pm.XmlSerializerAndParser; import android.content.res.Resources; import android.content.res.TypedArray; +import android.content.ComponentName; import android.content.Context; +import android.content.Intent; import android.util.AttributeSet; +import android.util.Log; import android.text.TextUtils; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlSerializer; import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; +import java.util.List; /** * A cache of services that export the {@link IAccountAuthenticator} interface. This cache @@ -63,11 +68,40 @@ import java.io.IOException; com.android.internal.R.styleable.AccountAuthenticator_smallIcon, 0); final int prefId = sa.getResourceId( com.android.internal.R.styleable.AccountAuthenticator_accountPreferences, 0); + + boolean customTokens = false; + try { + // In HC this will be an attribute in authenticator.xml, this is a workaround + // using meta-data to avoid changes to the API. + // If meta-data is absent the old behavior is preserved. + // Authenticator will know if AccountManager supports customTokens or not. + PackageManager pm = mContext.getPackageManager(); + List<ResolveInfo> resolveInfos = pm.queryIntentServices( + new Intent(AccountManager.ACTION_AUTHENTICATOR_INTENT), + PackageManager.GET_META_DATA); + for (ResolveInfo resolveInfo: resolveInfos) { + android.content.pm.ServiceInfo si = resolveInfo.serviceInfo; + if (!packageName.equals(si.packageName)) { + continue; + } + Object ctString = si.metaData.get(AccountManager.ACTION_AUTHENTICATOR_INTENT + + ".customTokens"); + if (ctString != null) { + customTokens = true; + } + } + } catch (Throwable t) { + // Protected against invalid data in meta or unexpected + // conditions - the authenticator will not have the new + // features. + Log.e(TAG, "Error getting customTokens metadata " + t); + } + if (TextUtils.isEmpty(accountType)) { return null; } - return new AuthenticatorDescription(accountType, packageName, labelId, iconId, - smallIconId, prefId); + return new AuthenticatorDescription(accountType, packageName, labelId, iconId, + smallIconId, prefId, customTokens); } finally { sa.recycle(); } diff --git a/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java index fd3a0d0..677b959 100644 --- a/core/java/android/accounts/AccountManager.java +++ b/core/java/android/accounts/AccountManager.java @@ -188,6 +188,24 @@ public class AccountManager { public static final String KEY_ERROR_CODE = "errorCode"; public static final String KEY_ERROR_MESSAGE = "errorMessage"; public static final String KEY_USERDATA = "userdata"; + /** + * Authenticators using 'customTokens' option will also get the UID of the + * caller + * @hide + */ + public static final String KEY_CALLER_UID = "callerUid"; + + /** + * @hide + */ + public static final String KEY_CALLER_PID = "callerPid"; + + /** + * Boolean, if set and 'customTokens' the authenticator is responsible for + * notifications. + * @hide + */ + public static final String KEY_NOTIFY_ON_FAILURE = "notifyOnAuthFailure"; public static final String ACTION_AUTHENTICATOR_INTENT = "android.accounts.AccountAuthenticator"; diff --git a/core/java/android/accounts/AccountManagerService.java b/core/java/android/accounts/AccountManagerService.java index 9a8cc15..5796042 100644 --- a/core/java/android/accounts/AccountManagerService.java +++ b/core/java/android/accounts/AccountManagerService.java @@ -91,6 +91,8 @@ public class AccountManagerService private final Context mContext; + private final PackageManager mPackageManager; + private HandlerThread mMessageThread; private final MessageHandler mMessageHandler; @@ -99,7 +101,6 @@ public class AccountManagerService private final AccountAuthenticatorCache mAuthenticatorCache; private final DatabaseHelper mOpenHelper; - private final SimWatcher mSimWatcher; private static final String TABLE_ACCOUNTS = "accounts"; private static final String ACCOUNTS_ID = "_id"; @@ -214,6 +215,7 @@ public class AccountManagerService public AccountManagerService(Context context) { mContext = context; + mPackageManager = context.getPackageManager(); mOpenHelper = new DatabaseHelper(mContext); @@ -224,7 +226,6 @@ public class AccountManagerService mAuthenticatorCache = new AccountAuthenticatorCache(mContext); mAuthenticatorCache.setListener(this, null /* Handler */); - mSimWatcher = new SimWatcher(mContext); sThis.set(this); validateAccounts(); @@ -520,6 +521,18 @@ public class AccountManagerService if (account == null) throw new IllegalArgumentException("account is null"); checkManageAccountsPermission(); long identityToken = clearCallingIdentity(); + + cancelNotification(getSigninRequiredNotificationId(account)); + synchronized(mCredentialsPermissionNotificationIds) { + for (Pair<Pair<Account, String>, Integer> pair: + mCredentialsPermissionNotificationIds.keySet()) { + if (account.equals(pair.first.first)) { + int id = mCredentialsPermissionNotificationIds.get(pair); + cancelNotification(id); + } + } + } + try { new RemoveAccountSession(response, account).bind(); } finally { @@ -842,19 +855,49 @@ public class AccountManagerService public void getAuthToken(IAccountManagerResponse response, final Account account, final String authTokenType, final boolean notifyOnAuthFailure, - final boolean expectActivityLaunch, final Bundle loginOptions) { + final boolean expectActivityLaunch, Bundle loginOptionsIn) { + if (Log.isLoggable(TAG, Log.VERBOSE)) { + Log.v(TAG, "getAuthToken: " + account + + ", response " + response + + ", authTokenType " + authTokenType + + ", notifyOnAuthFailure " + notifyOnAuthFailure + + ", expectActivityLaunch " + expectActivityLaunch + + ", caller's uid " + Binder.getCallingUid() + + ", pid " + Binder.getCallingPid()); + } if (response == null) throw new IllegalArgumentException("response is null"); if (account == null) throw new IllegalArgumentException("account is null"); if (authTokenType == null) throw new IllegalArgumentException("authTokenType is null"); checkBinderPermission(Manifest.permission.USE_CREDENTIALS); final int callerUid = Binder.getCallingUid(); - final boolean permissionGranted = permissionIsGranted(account, authTokenType, callerUid); + final int callerPid = Binder.getCallingPid(); + + AccountAuthenticatorCache.ServiceInfo<AuthenticatorDescription> authenticatorInfo = + mAuthenticatorCache.getServiceInfo( + AuthenticatorDescription.newKey(account.type)); + final boolean customTokens = + authenticatorInfo != null && authenticatorInfo.type.customTokens; + + // skip the check if customTokens + final boolean permissionGranted = customTokens || + permissionIsGranted(account, authTokenType, callerUid); + + final Bundle loginOptions = (loginOptionsIn == null) ? new Bundle() : + loginOptionsIn; + if (customTokens) { + // let authenticator know the identity of the caller + loginOptions.putInt(AccountManager.KEY_CALLER_UID, callerUid); + loginOptions.putInt(AccountManager.KEY_CALLER_PID, callerPid); + if (notifyOnAuthFailure) { + loginOptions.putBoolean(AccountManager.KEY_NOTIFY_ON_FAILURE, true); + } + } long identityToken = clearCallingIdentity(); try { // if the caller has permission, do the peek. otherwise go the more expensive // route of starting a Session - if (permissionGranted) { + if (!customTokens && permissionGranted) { String authToken = readAuthTokenFromDatabase(account, authTokenType); if (authToken != null) { Bundle result = new Bundle(); @@ -908,12 +951,14 @@ public class AccountManagerService "the type and name should not be empty"); return; } - saveAuthTokenToDatabase(new Account(name, type), - authTokenType, authToken); + if (!customTokens) { + saveAuthTokenToDatabase(new Account(name, type), + authTokenType, authToken); + } } Intent intent = result.getParcelable(AccountManager.KEY_INTENT); - if (intent != null && notifyOnAuthFailure) { + if (intent != null && notifyOnAuthFailure && !customTokens) { doNotification( account, result.getString(AccountManager.KEY_AUTH_FAILED_MESSAGE), intent); @@ -972,6 +1017,10 @@ public class AccountManagerService AccountAuthenticatorResponse response, String authTokenType, String authTokenLabel) { Intent intent = new Intent(mContext, GrantCredentialsPermissionActivity.class); + // See FLAG_ACTIVITY_NEW_TASK docs for limitations and benefits of the flag. + // Since it was set in Eclair+ we can't change it without breaking apps using + // the intent from a non-Activity context. + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.addCategory( String.valueOf(getCredentialPermissionNotificationId(account, authTokenType, uid))); @@ -1640,95 +1689,6 @@ public class AccountManagerService } } - private class SimWatcher extends BroadcastReceiver { - public SimWatcher(Context context) { - // Re-scan the SIM card when the SIM state changes, and also if - // the disk recovers from a full state (we may have failed to handle - // things properly while the disk was full). - final IntentFilter filter = new IntentFilter(); - filter.addAction(TelephonyIntents.ACTION_SIM_STATE_CHANGED); - filter.addAction(Intent.ACTION_DEVICE_STORAGE_OK); - context.registerReceiver(this, filter); - } - - /** - * Compare the IMSI to the one stored in the login service's - * database. If they differ, erase all passwords and - * authtokens (and store the new IMSI). - */ - @Override - public void onReceive(Context context, Intent intent) { - // Check IMSI on every update; nothing happens if the IMSI - // is missing or unchanged. - TelephonyManager telephonyManager = - (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); - if (telephonyManager == null) { - Log.w(TAG, "failed to get TelephonyManager"); - return; - } - String imsi = telephonyManager.getSubscriberId(); - - // If the subscriber ID is an empty string, don't do anything. - if (TextUtils.isEmpty(imsi)) return; - - // If the current IMSI matches what's stored, don't do anything. - String storedImsi = getMetaValue("imsi"); - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "current IMSI=" + imsi + "; stored IMSI=" + storedImsi); - } - if (imsi.equals(storedImsi)) return; - - // If a CDMA phone is unprovisioned, getSubscriberId() - // will return a different value, but we *don't* erase the - // passwords. We only erase them if it has a different - // subscriber ID once it's provisioned. - if (telephonyManager.getPhoneType() == TelephonyManager.PHONE_TYPE_CDMA) { - IBinder service = ServiceManager.checkService(Context.TELEPHONY_SERVICE); - if (service == null) { - Log.w(TAG, "call to checkService(TELEPHONY_SERVICE) failed"); - return; - } - ITelephony telephony = ITelephony.Stub.asInterface(service); - if (telephony == null) { - Log.w(TAG, "failed to get ITelephony interface"); - return; - } - boolean needsProvisioning; - try { - needsProvisioning = telephony.getCdmaNeedsProvisioning(); - } catch (RemoteException e) { - Log.w(TAG, "exception while checking provisioning", e); - // default to NOT wiping out the passwords - needsProvisioning = true; - } - if (needsProvisioning) { - // if the phone needs re-provisioning, don't do anything. - if (Log.isLoggable(TAG, Log.VERBOSE)) { - Log.v(TAG, "current IMSI=" + imsi + " (needs provisioning); stored IMSI=" + - storedImsi); - } - return; - } - } - - if (!imsi.equals(storedImsi) && !TextUtils.isEmpty(storedImsi)) { - Log.w(TAG, "wiping all passwords and authtokens because IMSI changed (" - + "stored=" + storedImsi + ", current=" + imsi + ")"); - SQLiteDatabase db = mOpenHelper.getWritableDatabase(); - db.beginTransaction(); - try { - db.execSQL("DELETE from " + TABLE_AUTHTOKENS); - db.execSQL("UPDATE " + TABLE_ACCOUNTS + " SET " + ACCOUNTS_PASSWORD + " = ''"); - sendAccountsChangedBroadcast(); - db.setTransactionSuccessful(); - } finally { - db.endTransaction(); - } - } - setMetaValue("imsi", imsi); - } - } - public IBinder onBind(Intent intent) { return asBinder(); } @@ -1849,12 +1809,12 @@ public class AccountManagerService } private boolean inSystemImage(int callerUid) { - String[] packages = mContext.getPackageManager().getPackagesForUid(callerUid); + String[] packages = mPackageManager.getPackagesForUid(callerUid); for (String name : packages) { try { - PackageInfo packageInfo = - mContext.getPackageManager().getPackageInfo(name, 0 /* flags */); - if ((packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { + PackageInfo packageInfo = mPackageManager.getPackageInfo(name, 0 /* flags */); + if (packageInfo != null + && (packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { return true; } } catch (PackageManager.NameNotFoundException e) { @@ -1872,7 +1832,7 @@ public class AccountManagerService && hasExplicitlyGrantedPermission(account, authTokenType); if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "checkGrantsOrCallingUidAgainstAuthenticator: caller uid " - + callerUid + ", account " + account + + callerUid + ", " + account + ": is authenticator? " + fromAuthenticator + ", has explicit permission? " + hasExplicitGrants); } @@ -1884,7 +1844,7 @@ public class AccountManagerService mAuthenticatorCache.getAllServices()) { if (serviceInfo.type.type.equals(accountType)) { return (serviceInfo.uid == callingUid) || - (mContext.getPackageManager().checkSignatures(serviceInfo.uid, callingUid) + (mPackageManager.checkSignatures(serviceInfo.uid, callingUid) == PackageManager.SIGNATURE_MATCH); } } diff --git a/core/java/android/accounts/AuthenticatorDescription.java b/core/java/android/accounts/AuthenticatorDescription.java index c651567..4d3769a 100644 --- a/core/java/android/accounts/AuthenticatorDescription.java +++ b/core/java/android/accounts/AuthenticatorDescription.java @@ -44,9 +44,16 @@ public class AuthenticatorDescription implements Parcelable { /** The package name that can be used to lookup the resources from above. */ final public String packageName; - /** A constructor for a full AuthenticatorDescription */ + /** Authenticator handles its own token caching and permission screen + * @hide + */ + final public boolean customTokens; + + /** A constructor for a full AuthenticatorDescription + * @hide + */ public AuthenticatorDescription(String type, String packageName, int labelId, int iconId, - int smallIconId, int prefId) { + int smallIconId, int prefId, boolean customTokens) { if (type == null) throw new IllegalArgumentException("type cannot be null"); if (packageName == null) throw new IllegalArgumentException("packageName cannot be null"); this.type = type; @@ -55,6 +62,12 @@ public class AuthenticatorDescription implements Parcelable { this.iconId = iconId; this.smallIconId = smallIconId; this.accountPreferencesId = prefId; + this.customTokens = customTokens; + } + + public AuthenticatorDescription(String type, String packageName, int labelId, int iconId, + int smallIconId, int prefId) { + this(type, packageName, labelId, iconId, smallIconId, prefId, false); } /** @@ -74,6 +87,7 @@ public class AuthenticatorDescription implements Parcelable { this.iconId = 0; this.smallIconId = 0; this.accountPreferencesId = 0; + this.customTokens = false; } private AuthenticatorDescription(Parcel source) { @@ -83,6 +97,7 @@ public class AuthenticatorDescription implements Parcelable { this.iconId = source.readInt(); this.smallIconId = source.readInt(); this.accountPreferencesId = source.readInt(); + this.customTokens = source.readByte() == 1; } /** @inheritDoc */ @@ -115,6 +130,7 @@ public class AuthenticatorDescription implements Parcelable { dest.writeInt(iconId); dest.writeInt(smallIconId); dest.writeInt(accountPreferencesId); + dest.writeByte((byte) (customTokens ? 1 : 0)); } /** Used to create the object from a parcel. */ diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index f535d61..0e473c9 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -61,6 +61,8 @@ import android.database.sqlite.SQLiteDatabase.CursorFactory; import android.graphics.Bitmap; import android.graphics.drawable.Drawable; import android.hardware.SensorManager; +import android.hardware.usb.IUsbManager; +import android.hardware.usb.UsbManager; import android.location.ILocationManager; import android.location.LocationManager; import android.media.AudioManager; @@ -188,6 +190,7 @@ class ContextImpl extends Context { private SearchManager mSearchManager = null; private SensorManager mSensorManager = null; private StorageManager mStorageManager = null; + private UsbManager mUsbManager = null; private Vibrator mVibrator = null; private LayoutInflater mLayoutInflater = null; private StatusBarManager mStatusBarManager = null; @@ -951,6 +954,8 @@ class ContextImpl extends Context { return getSensorManager(); } else if (STORAGE_SERVICE.equals(name)) { return getStorageManager(); + } else if (USB_SERVICE.equals(name)) { + return getUsbManager(); } else if (VIBRATOR_SERVICE.equals(name)) { return getVibrator(); } else if (STATUS_BAR_SERVICE.equals(name)) { @@ -1145,6 +1150,17 @@ class ContextImpl extends Context { return mStorageManager; } + private UsbManager getUsbManager() { + synchronized (mSync) { + if (mUsbManager == null) { + IBinder b = ServiceManager.getService(USB_SERVICE); + IUsbManager service = IUsbManager.Stub.asInterface(b); + mUsbManager = new UsbManager(this, service); + } + } + return mUsbManager; + } + private Vibrator getVibrator() { synchronized (mSync) { if (mVibrator == null) { diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java index da8c9e5..d70ec0b 100644 --- a/core/java/android/app/Dialog.java +++ b/core/java/android/app/Dialog.java @@ -823,7 +823,7 @@ public class Dialog implements DialogInterface, Window.Callback, // associate search with owner activity final ComponentName appName = getAssociatedActivity(); - if (appName != null) { + if (appName != null && searchManager.getSearchableInfo(appName) != null) { searchManager.startSearch(null, false, appName, null, false); dismiss(); return true; diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 85c29b8..ec2d640 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -1567,6 +1567,17 @@ public abstract class Context { public static final String SIP_SERVICE = "sip"; /** + * Use with {@link #getSystemService} to retrieve a {@link + * android.hardware.usb.UsbManager} for access to USB devices (as a USB host) + * and for controlling this device's behavior as a USB device. + * + * @see #getSystemService + * @see android.harware.usb.UsbManager + * @hide + */ + public static final String USB_SERVICE = "usb"; + + /** * Determine whether the given permission is allowed for a particular * process and user ID running in the system. * diff --git a/core/java/android/content/SyncManager.java b/core/java/android/content/SyncManager.java index 26b6ad7..5a83604 100644 --- a/core/java/android/content/SyncManager.java +++ b/core/java/android/content/SyncManager.java @@ -258,6 +258,7 @@ public class SyncManager implements OnAccountsUpdateListener { // DISCONNECTED for GPRS in any order. if we receive the CONNECTED first, and then // a DISCONNECTED, we want to make sure we set mDataConnectionIsConnected to true // since we still have a WiFi connection. + final boolean wasConnected = mDataConnectionIsConnected; switch (state) { case CONNECTED: mDataConnectionIsConnected = true; @@ -273,6 +274,12 @@ public class SyncManager implements OnAccountsUpdateListener { // ignore the rest of the states -- leave our boolean alone. } if (mDataConnectionIsConnected) { + if (!wasConnected) { + if (Log.isLoggable(TAG, Log.VERBOSE)) { + Log.v(TAG, "Reconnection detected: clearing all backoffs"); + } + mSyncStorageEngine.clearAllBackoffs(); + } sendCheckAlarmsMessage(); } } diff --git a/core/java/android/content/SyncStorageEngine.java b/core/java/android/content/SyncStorageEngine.java index e1a9dbb..faf1365 100644 --- a/core/java/android/content/SyncStorageEngine.java +++ b/core/java/android/content/SyncStorageEngine.java @@ -525,6 +525,32 @@ public class SyncStorageEngine extends Handler { } } + public void clearAllBackoffs() { + boolean changed = false; + synchronized (mAuthorities) { + for (AccountInfo accountInfo : mAccounts.values()) { + for (AuthorityInfo authorityInfo : accountInfo.authorities.values()) { + if (authorityInfo.backoffTime != NOT_IN_BACKOFF_MODE + || authorityInfo.backoffDelay != NOT_IN_BACKOFF_MODE) { + if (Log.isLoggable(TAG, Log.VERBOSE)) { + Log.v(TAG, "clearAllBackoffs:" + + " authority:" + authorityInfo.authority + + " account:" + accountInfo.account.name + + " backoffTime was: " + authorityInfo.backoffTime + + " backoffDelay was: " + authorityInfo.backoffDelay); + } + authorityInfo.backoffTime = NOT_IN_BACKOFF_MODE; + authorityInfo.backoffDelay = NOT_IN_BACKOFF_MODE; + changed = true; + } + } + } + } + + if (changed) { + reportChange(ContentResolver.SYNC_OBSERVER_TYPE_SETTINGS); + } + } public void setDelayUntilTime(Account account, String providerName, long delayUntil) { if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "setDelayUntil: " + account + ", provider " + providerName diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 922f8cd..a779925 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -785,6 +785,13 @@ public abstract class PackageManager { /** * Feature for {@link #getSystemAvailableFeatures} and + * {@link #hasSystemFeature}: The device supports connecting to USB accessories. + * @hide + */ + public static final String FEATURE_USB_ACCESSORY = "android.hardware.usb.accessory"; + + /** + * Feature for {@link #getSystemAvailableFeatures} and * {@link #hasSystemFeature}: The SIP API is enabled on the device. */ @SdkConstant(SdkConstantType.FEATURE) diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java index 73d9458..4702327 100644 --- a/core/java/android/content/res/AssetManager.java +++ b/core/java/android/content/res/AssetManager.java @@ -234,6 +234,7 @@ public final class AssetManager { StringBlock[] blocks = mStringBlocks; if (blocks == null) { ensureStringBlocks(); + blocks = mStringBlocks; } outValue.string = blocks[block].get(outValue.data); return true; diff --git a/core/java/android/hardware/SensorManager.java b/core/java/android/hardware/SensorManager.java index f079e42..b1a9349 100644 --- a/core/java/android/hardware/SensorManager.java +++ b/core/java/android/hardware/SensorManager.java @@ -1970,7 +1970,8 @@ public class SensorManager if (rotationVector.length == 4) { q0 = rotationVector[3]; } else { - q0 = (float)Math.sqrt(1 - q1*q1 - q2*q2 - q3*q3); + q0 = 1 - q1*q1 - q2*q2 - q3*q3; + q0 = (q0 > 0) ? (float)Math.sqrt(q0) : 0; } float sq_q1 = 2 * q1 * q1; @@ -2026,8 +2027,8 @@ public class SensorManager if (rv.length == 4) { Q[0] = rv[3]; } else { - //In this case, the w component of the quaternion is known to be a positive number - Q[0] = (float)Math.sqrt(1 - rv[0]*rv[0] - rv[1]*rv[1] - rv[2]*rv[2]); + Q[0] = 1 - rv[0]*rv[0] - rv[1]*rv[1] - rv[2]*rv[2]; + Q[0] = (Q[0] > 0) ? (float)Math.sqrt(Q[0]) : 0; } Q[1] = rv[0]; Q[2] = rv[1]; diff --git a/core/java/android/hardware/Usb.java b/core/java/android/hardware/Usb.java deleted file mode 100644 index 57271d4..0000000 --- a/core/java/android/hardware/Usb.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright (C) 2010 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; - -/** - * Class for accessing USB state information. - * @hide - */ -public class Usb { - /** - * Broadcast Action: A broadcast for USB connected events. - * - * The extras bundle will name/value pairs with the name of the function - * and a value of either {@link #USB_FUNCTION_ENABLED} or {@link #USB_FUNCTION_DISABLED}. - * Possible USB function names include {@link #USB_FUNCTION_MASS_STORAGE}, - * {@link #USB_FUNCTION_ADB}, {@link #USB_FUNCTION_RNDIS} and {@link #USB_FUNCTION_MTP}. - */ - public static final String ACTION_USB_CONNECTED = - "android.hardware.action.USB_CONNECTED"; - - /** - * Broadcast Action: A broadcast for USB disconnected events. - */ - public static final String ACTION_USB_DISCONNECTED = - "android.hardware.action.USB_DISCONNECTED"; - - /** - * Broadcast Action: A sticky broadcast for USB state change events. - * - * This is a sticky broadcast for clients that are interested in both USB connect and - * disconnect events. If you are only concerned with one or the other, you can use - * {@link #ACTION_USB_CONNECTED} or {@link #ACTION_USB_DISCONNECTED} to avoid receiving - * unnecessary broadcasts. The boolean {@link #USB_CONNECTED} extra indicates whether - * USB is connected or disconnected. - * The extras bundle will also contain name/value pairs with the name of the function - * and a value of either {@link #USB_FUNCTION_ENABLED} or {@link #USB_FUNCTION_DISABLED}. - * Possible USB function names include {@link #USB_FUNCTION_MASS_STORAGE}, - * {@link #USB_FUNCTION_ADB}, {@link #USB_FUNCTION_RNDIS} and {@link #USB_FUNCTION_MTP}. - */ - public static final String ACTION_USB_STATE = - "android.hardware.action.USB_STATE"; - - /** - * Boolean extra indicating whether USB is connected or disconnected. - * Used in extras for the {@link #ACTION_USB_STATE} broadcast. - */ - public static final String USB_CONNECTED = "connected"; - - /** - * Name of the USB mass storage USB function. - * Used in extras for the {@link #ACTION_USB_CONNECTED} broadcast - */ - public static final String USB_FUNCTION_MASS_STORAGE = "mass_storage"; - - /** - * Name of the adb USB function. - * Used in extras for the {@link #ACTION_USB_CONNECTED} broadcast - */ - public static final String USB_FUNCTION_ADB = "adb"; - - /** - * Name of the RNDIS ethernet USB function. - * Used in extras for the {@link #ACTION_USB_CONNECTED} broadcast - */ - public static final String USB_FUNCTION_RNDIS = "rndis"; - - /** - * Name of the MTP USB function. - * Used in extras for the {@link #ACTION_USB_CONNECTED} broadcast - */ - public static final String USB_FUNCTION_MTP = "mtp"; - - /** - * Value indicating that a USB function is enabled. - * Used in extras for the {@link #ACTION_USB_CONNECTED} broadcast - */ - public static final String USB_FUNCTION_ENABLED = "enabled"; - - /** - * Value indicating that a USB function is disabled. - * Used in extras for the {@link #ACTION_USB_CONNECTED} broadcast - */ - public static final String USB_FUNCTION_DISABLED = "disabled"; -} diff --git a/core/java/android/hardware/usb/IUsbManager.aidl b/core/java/android/hardware/usb/IUsbManager.aidl new file mode 100644 index 0000000..ffe557c --- /dev/null +++ b/core/java/android/hardware/usb/IUsbManager.aidl @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2010 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.usb; + +import android.app.PendingIntent; +import android.hardware.usb.UsbAccessory; +import android.os.Bundle; +import android.os.ParcelFileDescriptor; + +/** @hide */ +interface IUsbManager +{ + /* Returns the currently attached USB accessory */ + UsbAccessory getCurrentAccessory(); + + /* Returns a file descriptor for communicating with the USB accessory. + * This file descriptor can be used with standard Java file operations. + */ + ParcelFileDescriptor openAccessory(in UsbAccessory accessory); + + /* Sets the default package for a USB accessory + * (or clears it if the package name is null) + */ + void setAccessoryPackage(in UsbAccessory accessory, String packageName); + + /* Returns true if the caller has permission to access the accessory. */ + boolean hasAccessoryPermission(in UsbAccessory accessory); + + /* Requests permission for the given package to access the accessory. + * Will display a system dialog to query the user if permission + * had not already been given. Result is returned via pi. + */ + void requestAccessoryPermission(in UsbAccessory accessory, String packageName, + in PendingIntent pi); + + /* Grants permission for the given UID to access the accessory */ + void grantAccessoryPermission(in UsbAccessory accessory, int uid); + + /* Returns true if the USB manager has default preferences or permissions for the package */ + boolean hasDefaults(String packageName); + + /* Clears default preferences and permissions for the package */ + void clearDefaults(String packageName); +} diff --git a/core/java/android/hardware/usb/UsbAccessory.aidl b/core/java/android/hardware/usb/UsbAccessory.aidl new file mode 100644 index 0000000..1c15f1c --- /dev/null +++ b/core/java/android/hardware/usb/UsbAccessory.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2011, 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.usb; + +parcelable UsbAccessory; diff --git a/core/java/android/hardware/usb/UsbAccessory.java b/core/java/android/hardware/usb/UsbAccessory.java new file mode 100644 index 0000000..8938463 --- /dev/null +++ b/core/java/android/hardware/usb/UsbAccessory.java @@ -0,0 +1,192 @@ +/* + * Copyright (C) 2011 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.usb; + +import android.os.Bundle; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.Log; + +/** + * A class representing a USB accessory. + * @hide + */ +public class UsbAccessory implements Parcelable { + + private static final String TAG = "UsbAccessory"; + + private final String mManufacturer; + private final String mModel; + private final String mDescription; + private final String mVersion; + private final String mUri; + private final String mSerial; + + /** + * UsbAccessory should only be instantiated by UsbService implementation + * @hide + */ + public UsbAccessory(String manufacturer, String model, String description, + String version, String uri, String serial) { + mManufacturer = manufacturer; + mModel = model; + mDescription = description; + mVersion = version; + mUri = uri; + mSerial = serial; + } + + /** + * UsbAccessory should only be instantiated by UsbService implementation + * @hide + */ + public UsbAccessory(String[] strings) { + mManufacturer = strings[0]; + mModel = strings[1]; + mDescription = strings[2]; + mVersion = strings[3]; + mUri = strings[4]; + mSerial = strings[5]; + } + + /** + * Returns the manufacturer of the accessory. + * + * @return the accessory manufacturer + */ + public String getManufacturer() { + return mManufacturer; + } + + /** + * Returns the model name of the accessory. + * + * @return the accessory model + */ + public String getModel() { + return mModel; + } + + /** + * Returns a user visible description of the accessory. + * + * @return the accessory description + */ + public String getDescription() { + return mDescription; + } + + /** + * Returns the version of the accessory. + * + * @return the accessory version + */ + public String getVersion() { + return mVersion; + } + + /** + * Returns the URI for the accessory. + * This is an optional URI that might show information about the accessory + * or provide the option to download an application for the accessory + * + * @return the accessory URI + */ + public String getUri() { + return mUri; + } + + /** + * Returns the unique serial number for the accessory. + * This is an optional serial number that can be used to differentiate + * between individual accessories of the same model and manufacturer + * + * @return the unique serial number + */ + public String getSerial() { + return mSerial; + } + + private static boolean compare(String s1, String s2) { + if (s1 == null) return (s2 == null); + return s1.equals(s2); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof UsbAccessory) { + UsbAccessory accessory = (UsbAccessory)obj; + return (compare(mManufacturer, accessory.getManufacturer()) && + compare(mModel, accessory.getModel()) && + compare(mDescription, accessory.getDescription()) && + compare(mVersion, accessory.getVersion()) && + compare(mUri, accessory.getUri()) && + compare(mSerial, accessory.getSerial())); + } + return false; + } + + @Override + public int hashCode() { + return ((mManufacturer == null ? 0 : mManufacturer.hashCode()) ^ + (mModel == null ? 0 : mModel.hashCode()) ^ + (mDescription == null ? 0 : mDescription.hashCode()) ^ + (mVersion == null ? 0 : mVersion.hashCode()) ^ + (mUri == null ? 0 : mUri.hashCode()) ^ + (mSerial == null ? 0 : mSerial.hashCode())); + } + + @Override + public String toString() { + return "UsbAccessory[mManufacturer=" + mManufacturer + + ", mModel=" + mModel + + ", mDescription=" + mDescription + + ", mVersion=" + mVersion + + ", mUri=" + mUri + + ", mSerial=" + mSerial + "]"; + } + + public static final Parcelable.Creator<UsbAccessory> CREATOR = + new Parcelable.Creator<UsbAccessory>() { + public UsbAccessory createFromParcel(Parcel in) { + String manufacturer = in.readString(); + String model = in.readString(); + String description = in.readString(); + String version = in.readString(); + String uri = in.readString(); + String serial = in.readString(); + return new UsbAccessory(manufacturer, model, description, version, uri, serial); + } + + public UsbAccessory[] newArray(int size) { + return new UsbAccessory[size]; + } + }; + + public int describeContents() { + return 0; + } + + public void writeToParcel(Parcel parcel, int flags) { + parcel.writeString(mManufacturer); + parcel.writeString(mModel); + parcel.writeString(mDescription); + parcel.writeString(mVersion); + parcel.writeString(mUri); + parcel.writeString(mSerial); + } +} diff --git a/core/java/android/hardware/usb/UsbManager.java b/core/java/android/hardware/usb/UsbManager.java new file mode 100644 index 0000000..ea820f9 --- /dev/null +++ b/core/java/android/hardware/usb/UsbManager.java @@ -0,0 +1,295 @@ +/* + * Copyright (C) 2010 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.usb; + +import android.app.PendingIntent; +import android.content.Context; +import android.os.Bundle; +import android.os.ParcelFileDescriptor; +import android.os.RemoteException; +import android.util.Log; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.HashMap; + +/** + * This class allows you to access the state of USB. + * + * <p>You can obtain an instance of this class by calling + * {@link android.content.Context#getSystemService(java.lang.String) Context.getSystemService()}. + * + * {@samplecode + * UsbManager manager = (UsbManager) getSystemService(Context.USB_SERVICE); + * } + * @hide + */ +public class UsbManager { + private static final String TAG = "UsbManager"; + + /** + * Broadcast Action: A sticky broadcast for USB state change events when in device mode. + * + * This is a sticky broadcast for clients that includes USB connected/disconnected state, + * <ul> + * <li> {@link #USB_CONNECTED} boolean indicating whether USB is connected or disconnected. + * <li> {@link #USB_CONFIGURATION} a Bundle containing name/value pairs where the name + * is the name of a USB function and the value is either {@link #USB_FUNCTION_ENABLED} + * or {@link #USB_FUNCTION_DISABLED}. The possible function names include + * {@link #USB_FUNCTION_MASS_STORAGE}, {@link #USB_FUNCTION_ADB}, {@link #USB_FUNCTION_RNDIS}, + * {@link #USB_FUNCTION_MTP} and {@link #USB_FUNCTION_ACCESSORY}. + * </ul> + */ + public static final String ACTION_USB_STATE = + "android.hardware.usb.action.USB_STATE"; + + /** + * Broadcast Action: A broadcast for USB accessory attached event. + * + * This intent is sent when a USB accessory is attached. + * <ul> + * <li> {@link #EXTRA_ACCESSORY} containing the {@link android.hardware.usb.UsbAccessory} + * for the attached accessory + * </ul> + */ + public static final String ACTION_USB_ACCESSORY_ATTACHED = + "android.hardware.usb.action.USB_ACCESSORY_ATTACHED"; + + /** + * Broadcast Action: A broadcast for USB accessory detached event. + * + * This intent is sent when a USB accessory is detached. + * <ul> + * <li> {@link #EXTRA_ACCESSORY} containing the {@link UsbAccessory} + * for the attached accessory that was detached + * </ul> + */ + public static final String ACTION_USB_ACCESSORY_DETACHED = + "android.hardware.usb.action.USB_ACCESSORY_DETACHED"; + + /** + * Boolean extra indicating whether USB is connected or disconnected. + * Used in extras for the {@link #ACTION_USB_STATE} broadcast. + */ + public static final String USB_CONNECTED = "connected"; + + /** + * Integer extra containing currently set USB configuration. + * Used in extras for the {@link #ACTION_USB_STATE} broadcast. + */ + public static final String USB_CONFIGURATION = "configuration"; + + /** + * Name of the USB mass storage USB function. + * Used in extras for the {@link #ACTION_USB_STATE} broadcast + */ + public static final String USB_FUNCTION_MASS_STORAGE = "mass_storage"; + + /** + * Name of the adb USB function. + * Used in extras for the {@link #ACTION_USB_STATE} broadcast + */ + public static final String USB_FUNCTION_ADB = "adb"; + + /** + * Name of the RNDIS ethernet USB function. + * Used in extras for the {@link #ACTION_USB_STATE} broadcast + */ + public static final String USB_FUNCTION_RNDIS = "rndis"; + + /** + * Name of the MTP USB function. + * Used in extras for the {@link #ACTION_USB_STATE} broadcast + */ + public static final String USB_FUNCTION_MTP = "mtp"; + + /** + * Name of the Accessory USB function. + * Used in extras for the {@link #ACTION_USB_STATE} broadcast + */ + public static final String USB_FUNCTION_ACCESSORY = "accessory"; + + /** + * Value indicating that a USB function is enabled. + * Used in {@link #USB_CONFIGURATION} extras bundle for the + * {@link #ACTION_USB_STATE} broadcast + */ + public static final String USB_FUNCTION_ENABLED = "enabled"; + + /** + * Value indicating that a USB function is disabled. + * Used in {@link #USB_CONFIGURATION} extras bundle for the + * {@link #ACTION_USB_STATE} broadcast + */ + public static final String USB_FUNCTION_DISABLED = "disabled"; + + /** + * Name of extra for {@link #ACTION_USB_ACCESSORY_ATTACHED} and + * {@link #ACTION_USB_ACCESSORY_DETACHED} broadcasts + * containing the UsbAccessory object for the accessory. + */ + public static final String EXTRA_ACCESSORY = "accessory"; + + /** + * Name of extra added to the {@link android.app.PendingIntent} + * passed into {@link #requestPermission(UsbDevice, PendingIntent)} + * or {@link #requestPermission(UsbAccessory, PendingIntent)} + * containing a boolean value indicating whether the user granted permission or not. + */ + public static final String EXTRA_PERMISSION_GRANTED = "permission"; + + private final Context mContext; + private final IUsbManager mService; + + /** + * {@hide} + */ + public UsbManager(Context context, IUsbManager service) { + mContext = context; + mService = service; + } + + /** + * Returns a list of currently attached USB accessories. + * (in the current implementation there can be at most one) + * + * @return list of USB accessories, or null if none are attached. + */ + public UsbAccessory[] getAccessoryList() { + try { + UsbAccessory accessory = mService.getCurrentAccessory(); + if (accessory == null) { + return null; + } else { + return new UsbAccessory[] { accessory }; + } + } catch (RemoteException e) { + Log.e(TAG, "RemoteException in getAccessoryList", e); + return null; + } + } + + /** + * Opens a file descriptor for reading and writing data to the USB accessory. + * + * @param accessory the USB accessory to open + * @return file descriptor, or null if the accessor could not be opened. + */ + public ParcelFileDescriptor openAccessory(UsbAccessory accessory) { + try { + return mService.openAccessory(accessory); + } catch (RemoteException e) { + Log.e(TAG, "RemoteException in openAccessory", e); + return null; + } + } + + /** + * Returns true if the caller has permission to access the accessory. + * Permission might have been granted temporarily via + * {@link #requestPermission(UsbAccessory, PendingIntent)} or + * by the user choosing the caller as the default application for the accessory. + * + * @param accessory to check permissions for + * @return true if caller has permission + */ + public boolean hasPermission(UsbAccessory accessory) { + try { + return mService.hasAccessoryPermission(accessory); + } catch (RemoteException e) { + Log.e(TAG, "RemoteException in hasPermission", e); + return false; + } + } + + /** + * Requests temporary permission for the given package to access the accessory. + * This may result in a system dialog being displayed to the user + * if permission had not already been granted. + * Success or failure is returned via the {@link android.app.PendingIntent} pi. + * If successful, this grants the caller permission to access the accessory only + * until the device is disconnected. + * + * The following extras will be added to pi: + * <ul> + * <li> {@link #EXTRA_ACCESSORY} containing the accessory passed into this call + * <li> {@link #EXTRA_PERMISSION_GRANTED} containing boolean indicating whether + * permission was granted by the user + * </ul> + * + * @param accessory to request permissions for + * @param pi PendingIntent for returning result + */ + public void requestPermission(UsbAccessory accessory, PendingIntent pi) { + try { + mService.requestAccessoryPermission(accessory, mContext.getPackageName(), pi); + } catch (RemoteException e) { + Log.e(TAG, "RemoteException in requestPermission", e); + } + } + + private static File getFunctionEnableFile(String function) { + return new File("/sys/class/usb_composite/" + function + "/enable"); + } + + /** + * Returns true if the specified USB function is supported by the kernel. + * Note that a USB function maybe supported but disabled. + * + * @param function name of the USB function + * @return true if the USB function is supported. + */ + public static boolean isFunctionSupported(String function) { + return getFunctionEnableFile(function).exists(); + } + + /** + * Returns true if the specified USB function is currently enabled. + * + * @param function name of the USB function + * @return true if the USB function is enabled. + */ + public static boolean isFunctionEnabled(String function) { + try { + FileInputStream stream = new FileInputStream(getFunctionEnableFile(function)); + boolean enabled = (stream.read() == '1'); + stream.close(); + return enabled; + } catch (IOException e) { + return false; + } + } + + /** + * Enables or disables a USB function. + * + * @hide + */ + public static boolean setFunctionEnabled(String function, boolean enable) { + try { + FileOutputStream stream = new FileOutputStream(getFunctionEnableFile(function)); + stream.write(enable ? '1' : '0'); + stream.close(); + return true; + } catch (IOException e) { + return false; + } + } +} diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java index a43914a..494e922 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -222,9 +222,9 @@ public class ConnectivityManager /** {@hide} */ public static final int TYPE_ETHERNET = 9; /** {@hide} TODO: Need to adjust this for WiMAX. */ - public static final int MAX_RADIO_TYPE = TYPE_WIFI; + public static final int MAX_RADIO_TYPE = TYPE_ETHERNET; /** {@hide} TODO: Need to adjust this for WiMAX. */ - public static final int MAX_NETWORK_TYPE = TYPE_MOBILE_HIPRI; + public static final int MAX_NETWORK_TYPE = TYPE_ETHERNET; public static final int DEFAULT_NETWORK_PREFERENCE = TYPE_WIFI; diff --git a/core/java/android/net/InterfaceConfiguration.java b/core/java/android/net/InterfaceConfiguration.java index 915c5d7..dcea3af 100644 --- a/core/java/android/net/InterfaceConfiguration.java +++ b/core/java/android/net/InterfaceConfiguration.java @@ -51,6 +51,24 @@ public class InterfaceConfiguration implements Parcelable { append(addr & 0xff); } + /** + * This function determines if the interface is up and has a valid IP + * configuration (IP address has a non zero octet). + * + * Note: It is supposed to be quick and hence should not initiate + * any network activity + */ + public boolean isActive() { + try { + if(interfaceFlags.contains("up")) { + if (ipAddr != 0) return true; + } + } catch (NullPointerException e) { + return false; + } + return false; + } + /** Implement the Parcelable interface {@hide} */ public int describeContents() { return 0; diff --git a/core/java/android/net/MobileDataStateTracker.java b/core/java/android/net/MobileDataStateTracker.java index 32c2d64..04b0f12 100644 --- a/core/java/android/net/MobileDataStateTracker.java +++ b/core/java/android/net/MobileDataStateTracker.java @@ -247,6 +247,9 @@ public class MobileDataStateTracker extends NetworkStateTracker { Log.d(TAG, "CONNECTED event did not supply interface name."); } mDefaultGatewayAddr = intent.getIntExtra(Phone.DATA_GATEWAY_KEY, 0); + if (mDefaultGatewayAddr == 0) { + Log.d(TAG, "CONNECTED event did not supply a default gateway."); + } setDetailedState(DetailedState.CONNECTED, reason, apnName); break; } @@ -385,6 +388,7 @@ public class MobileDataStateTracker extends NetworkStateTracker { intent.putExtra(Phone.DATA_APN_KEY, mApnName); intent.putExtra(Phone.DATA_IFACE_NAME_KEY, mInterfaceName); intent.putExtra(Phone.NETWORK_UNAVAILABLE_KEY, false); + intent.putExtra(Phone.DATA_GATEWAY_KEY, mDefaultGatewayAddr); if (mStateReceiver != null) mStateReceiver.onReceive(mContext, intent); break; case Phone.APN_REQUEST_STARTED: diff --git a/core/java/android/net/NetworkStateTracker.java b/core/java/android/net/NetworkStateTracker.java index 9140dc9..233ad21 100644 --- a/core/java/android/net/NetworkStateTracker.java +++ b/core/java/android/net/NetworkStateTracker.java @@ -172,6 +172,7 @@ public abstract class NetworkStateTracker extends Handler { if (inetAddress == null) { if (DBG) Log.d(TAG, " Unable to add default route. mDefaultGatewayAddr Error"); } else { + NetworkUtils.addHostRoute(mInterfaceName, inetAddress, null); if (!NetworkUtils.addDefaultRoute(mInterfaceName, inetAddress) && DBG) { Log.d(TAG, " Unable to add default route."); } @@ -423,4 +424,7 @@ public abstract class NetworkStateTracker extends Handler { public void interpretScanResultsAvailable() { } + public String getInterfaceName() { + return mInterfaceName; + } } diff --git a/core/java/android/net/NetworkUtils.java b/core/java/android/net/NetworkUtils.java index 5d4b099..8bdfdf9 100644 --- a/core/java/android/net/NetworkUtils.java +++ b/core/java/android/net/NetworkUtils.java @@ -209,4 +209,16 @@ public class NetworkUtils { } return result; } + + /** + * Start the DHCP renew service for wimax, + * This call blocks until it obtains a result (either success + * or failure) from the daemon. + * @param interfaceName the name of the interface to configure + * @param ipInfo if the request succeeds, this object is filled in with + * the IP address information. + * @return {@code true} for success, {@code false} for failure + * {@hide} + */ + public native static boolean runDhcpRenew(String interfaceName, DhcpInfo ipInfo); } diff --git a/core/java/android/net/wimax/WimaxManagerConstants.java b/core/java/android/net/wimax/WimaxManagerConstants.java new file mode 100644 index 0000000..5ec4e96 --- /dev/null +++ b/core/java/android/net/wimax/WimaxManagerConstants.java @@ -0,0 +1,103 @@ +package android.net.wimax; + +/** + * {@hide} + */ +public class WimaxManagerConstants +{ + + /** + * Used by android.net.wimax.WimaxManager for handling management of + * Wimax access. + */ + public static final String WIMAX_SERVICE = "WiMax"; + + /** + * Broadcast intent action indicating that Wimax has been enabled, disabled, + * enabling, disabling, or unknown. One extra provides this state as an int. + * Another extra provides the previous state, if available. + */ + public static final String WIMAX_ENABLED_STATUS_CHANGED = + "android.net.wimax.WIMAX_STATUS_CHANGED"; + + /** + * The lookup key for an int that indicates whether Wimax is enabled, + * disabled, enabling, disabling, or unknown. + */ + public static final String EXTRA_WIMAX_STATUS = "wimax_status"; + + /** + * Broadcast intent action indicating that Wimax state has been changed + * state could be scanning, connecting, connected, disconnecting, disconnected + * initializing, initialized, unknown and ready. One extra provides this state as an int. + * Another extra provides the previous state, if available. + */ + public static final String WIMAX_STATE_CHANGED_ACTION = + "android.net.wimax.WIMAX_STATE_CHANGE"; + + /** + * Broadcast intent action indicating that Wimax signal level has been changed. + * Level varies from 0 to 3. + */ + public static final String SIGNAL_LEVEL_CHANGED_ACTION = + "android.net.wimax.SIGNAL_LEVEL_CHANGED"; + + /** + * The lookup key for an int that indicates whether Wimax state is + * scanning, connecting, connected, disconnecting, disconnected + * initializing, initialized, unknown and ready. + */ + public static final String EXTRA_WIMAX_STATE = "WimaxState"; + + /** + * The lookup key for an int that indicates whether state of Wimax + * is idle. + */ + public static final String EXTRA_WIMAX_STATE_DETAIL = "WimaxStateDetail"; + + /** + * The lookup key for an int that indicates Wimax signal level. + */ + public static final String EXTRA_NEW_SIGNAL_LEVEL = "newSignalLevel"; + + /** + * Indicatates Wimax is disabled. + */ + public static final int WIMAX_STATUS_DISABLED = 1; + + /** + * Indicatates Wimax is enabled. + */ + public static final int WIMAX_STATUS_ENABLED = 3; + + /** + * Indicatates Wimax status is known. + */ + public static final int WIMAX_STATUS_UNKNOWN = 4; + + /** + * Indicatates Wimax is in idle state. + */ + public static final int WIMAX_IDLE = 6; + + /** + * Indicatates Wimax is being deregistered. + */ + public static final int WIMAX_DEREGISTRATION = 8; + + /** + * Indicatates wimax state is unknown. + */ + public static final int WIMAX_STATE_UNKNOWN = 0; + + /** + * Indicatates wimax state is connected. + */ + public static final int WIMAX_STATE_CONNECTED = 7; + + /** + * Indicatates wimax state is disconnected. + */ + public static final int WIMAX_STATE_DISCONNECTED = 9; + +} diff --git a/core/java/android/nfc/INfcSecureElement.aidl b/core/java/android/nfc/ApduList.aidl index aa98dd2..f6236b2 100755..100644 --- a/core/java/android/nfc/INfcSecureElement.aidl +++ b/core/java/android/nfc/ApduList.aidl @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 The Android Open Source Project + * Copyright (C) 2011 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. @@ -16,13 +16,4 @@ package android.nfc; -/** - * {@hide} - */ -interface INfcSecureElement { - int openSecureElementConnection(); - int closeSecureElementConnection(int nativeHandle); - byte[] exchangeAPDU(int nativeHandle, in byte[] data); - int[] getSecureElementTechList(int nativeHandle); - byte[] getSecureElementUid(int nativeHandle); -}
\ No newline at end of file +parcelable ApduList;
\ No newline at end of file diff --git a/core/java/android/nfc/ApduList.java b/core/java/android/nfc/ApduList.java new file mode 100644 index 0000000..85b0547 --- /dev/null +++ b/core/java/android/nfc/ApduList.java @@ -0,0 +1,68 @@ +package android.nfc; + +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.ArrayList; +import java.util.List; + +/** + * @hide + */ +public class ApduList implements Parcelable { + + private ArrayList<byte[]> commands = new ArrayList<byte[]>(); + + public ApduList() { + } + + public void add(byte[] command) { + commands.add(command); + } + + public List<byte[]> get() { + return commands; + } + + public static final Parcelable.Creator<ApduList> CREATOR = + new Parcelable.Creator<ApduList>() { + @Override + public ApduList createFromParcel(Parcel in) { + return new ApduList(in); + } + + @Override + public ApduList[] newArray(int size) { + return new ApduList[size]; + } + }; + + private ApduList(Parcel in) { + int count = in.readInt(); + + for (int i = 0 ; i < count ; i++) { + + int length = in.readInt(); + byte[] cmd = new byte[length]; + in.readByteArray(cmd); + commands.add(cmd); + } + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(commands.size()); + + for (byte[] cmd : commands) { + dest.writeInt(cmd.length); + dest.writeByteArray(cmd); + } + } +} + + diff --git a/core/java/android/nfc/INfcAdapter.aidl b/core/java/android/nfc/INfcAdapter.aidl index d439a48..870127c 100644 --- a/core/java/android/nfc/INfcAdapter.aidl +++ b/core/java/android/nfc/INfcAdapter.aidl @@ -28,7 +28,7 @@ import android.nfc.ILlcpConnectionlessSocket; import android.nfc.INfcTag; import android.nfc.IP2pTarget; import android.nfc.IP2pInitiator; -import android.nfc.INfcSecureElement; +import android.nfc.INfcAdapterExtras; /** * @hide @@ -41,13 +41,12 @@ interface INfcAdapter INfcTag getNfcTagInterface(); IP2pTarget getP2pTargetInterface(); IP2pInitiator getP2pInitiatorInterface(); - INfcSecureElement getNfcSecureElementInterface(); + INfcAdapterExtras getNfcAdapterExtrasInterface(); // NfcAdapter-class related methods boolean isEnabled(); NdefMessage localGet(); void localSet(in NdefMessage message); - void openTagConnection(in Tag tag); void enableForegroundDispatch(in ComponentName activity, in PendingIntent intent, in IntentFilter[] filters, in TechListParcel techLists); void disableForegroundDispatch(in ComponentName activity); @@ -59,12 +58,8 @@ interface INfcAdapter int createLlcpConnectionlessSocket(int sap); int createLlcpServiceSocket(int sap, String sn, int miu, int rw, int linearBufferLength); int createLlcpSocket(int sap, int miu, int rw, int linearBufferLength); - int deselectSecureElement(); boolean disable(); boolean enable(); String getProperties(String param); - int[] getSecureElementList(); - int getSelectedSecureElement(); - int selectSecureElement(int seId); int setProperties(String param, String value); -}
\ No newline at end of file +} diff --git a/core/java/android/nfc/INfcAdapterExtras.aidl b/core/java/android/nfc/INfcAdapterExtras.aidl new file mode 100755 index 0000000..8677a50 --- /dev/null +++ b/core/java/android/nfc/INfcAdapterExtras.aidl @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2011 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.nfc; + +import android.nfc.ApduList; +import android.os.Bundle; + + +/** + * {@hide} + */ +interface INfcAdapterExtras { + Bundle open(IBinder b); + Bundle close(); + Bundle transceive(in byte[] data_in); + int getCardEmulationRoute(); + void setCardEmulationRoute(int route); + void registerTearDownApdus(String packageName, in ApduList apdu); + void unregisterTearDownApdus(String packageName); +} diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java index 622bcdb..4689804 100644 --- a/core/java/android/nfc/NfcAdapter.java +++ b/core/java/android/nfc/NfcAdapter.java @@ -157,31 +157,6 @@ public final class NfcAdapter { public static final String EXTRA_ID = "android.nfc.extra.ID"; /** - * Broadcast Action: a transaction with a secure element has been detected. - * <p> - * Always contains the extra field - * {@link android.nfc.NfcAdapter#EXTRA_AID} - * @hide - */ - @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_TRANSACTION_DETECTED = - "android.nfc.action.TRANSACTION_DETECTED"; - - /** - * Broadcast Action: an RF field ON has been detected. - * @hide - */ - public static final String ACTION_RF_FIELD_ON_DETECTED = - "android.nfc.action.RF_FIELD_ON_DETECTED"; - - /** - * Broadcast Action: an RF Field OFF has been detected. - * @hide - */ - public static final String ACTION_RF_FIELD_OFF_DETECTED = - "android.nfc.action.RF_FIELD_OFF_DETECTED"; - - /** * Broadcast Action: an adapter's state changed between enabled and disabled. * * The new value is stored in the extra EXTRA_NEW_BOOLEAN_STATE and just contains @@ -201,15 +176,6 @@ public final class NfcAdapter { public static final String EXTRA_NEW_BOOLEAN_STATE = "android.nfc.isEnabled"; /** - * Mandatory byte array extra field in - * {@link android.nfc.NfcAdapter#ACTION_TRANSACTION_DETECTED}. - * <p> - * Contains the AID of the applet involved in the transaction. - * @hide - */ - public static final String EXTRA_AID = "android.nfc.extra.AID"; - - /** * LLCP link status: The LLCP link is activated. * @hide */ @@ -691,14 +657,13 @@ public final class NfcAdapter { } /** - * Create an Nfc Secure Element Connection * @hide */ - public NfcSecureElement createNfcSecureElementConnection() { + public INfcAdapterExtras getNfcAdapterExtrasInterface() { try { - return new NfcSecureElement(sService.getNfcSecureElementInterface()); + return sService.getNfcAdapterExtrasInterface(); } catch (RemoteException e) { - Log.e(TAG, "createNfcSecureElementConnection failed", e); + attemptDeadServiceRecovery(e); return null; } } diff --git a/core/java/android/nfc/NfcSecureElement.java b/core/java/android/nfc/NfcSecureElement.java deleted file mode 100755 index 3b5f39e..0000000 --- a/core/java/android/nfc/NfcSecureElement.java +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Copyright (C) 2010 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.nfc; - -import android.nfc.tech.TagTechnology; -import android.os.RemoteException; -import android.util.Log; - -import java.io.IOException; - -//import android.util.Log; - -/** - * This class provides the primary API for managing all aspects Secure Element. - * Get an instance of this class by calling - * Context.getSystemService(Context.NFC_SERVICE). - * @hide - */ -public final class NfcSecureElement { - - private static final String TAG = "NfcSecureElement"; - - private INfcSecureElement mService; - - - /** - * @hide - */ - public NfcSecureElement(INfcSecureElement mSecureElementService) { - mService = mSecureElementService; - } - - public int openSecureElementConnection(String seType) throws IOException { - if (seType.equals("SmartMX")) { - try { - int handle = mService.openSecureElementConnection(); - // Handle potential errors - if (handle != 0) { - return handle; - } else { - throw new IOException("SmartMX connection not allowed"); - } - } catch (RemoteException e) { - Log.e(TAG, "RemoteException in openSecureElementConnection(): ", e); - return 0; - } - - } else if (seType.equals("UICC")) { - return 0; - } else { - throw new IOException("Wrong Secure Element type"); - } - } - - - public byte [] exchangeAPDU(int handle,byte [] data) throws IOException { - - - // Perform exchange APDU - try { - byte[] response = mService.exchangeAPDU(handle, data); - // Handle potential errors - if (response == null) { - throw new IOException("Exchange APDU failed"); - } - return response; - } catch (RemoteException e) { - Log.e(TAG, "RemoteException in exchangeAPDU(): ", e); - return null; - } - } - - public void closeSecureElementConnection(int handle) throws IOException { - - try { - int status = mService.closeSecureElementConnection(handle); - // Handle potential errors - if (ErrorCodes.isError(status)) { - throw new IOException("Error during the conection close"); - }; - } catch (RemoteException e) { - Log.e(TAG, "RemoteException in closeSecureElement(): ", e); - } - } - - - /** - * Returns target type. constants. - * - * @return Secure Element technology type. The possible values are defined in - * {@link TagTechnology} - * - */ - public int[] getSecureElementTechList(int handle) throws IOException { - try { - return mService.getSecureElementTechList(handle); - } catch (RemoteException e) { - Log.e(TAG, "RemoteException in getType(): ", e); - return null; - } - } - - /** - * Returns Secure Element UID. - * - * @return Secure Element UID. - */ - public byte[] getSecureElementUid(int handle) throws IOException { - - byte[] uid = null; - try { - uid = mService.getSecureElementUid(handle); - // Handle potential errors - if (uid == null) { - throw new IOException("Get Secure Element UID failed"); - } - return uid; - } catch (RemoteException e) { - Log.e(TAG, "RemoteException in getType(): ", e); - return null; - } - } - -} diff --git a/core/java/android/nfc/tech/Ndef.java b/core/java/android/nfc/tech/Ndef.java index 6727d6a..e4daa57 100644 --- a/core/java/android/nfc/tech/Ndef.java +++ b/core/java/android/nfc/tech/Ndef.java @@ -103,6 +103,8 @@ public final class Ndef extends BasicTagTechnology { public static final int TYPE_4 = 4; /** @hide */ public static final int TYPE_MIFARE_CLASSIC = 101; + /** @hide */ + public static final int TYPE_ICODE_SLI = 102; /** @hide */ public static final String UNKNOWN = "android.ndef.unknown"; @@ -117,6 +119,11 @@ public final class Ndef extends BasicTagTechnology { public static final String NFC_FORUM_TYPE_4 = "org.nfcforum.ndef.type4"; /** NDEF on MIFARE Classic */ public static final String MIFARE_CLASSIC = "com.nxp.ndef.mifareclassic"; + /** + * NDEF on iCODE SLI + * @hide + */ + public static final String ICODE_SLI = "com.nxp.ndef.icodesli"; private final int mMaxNdefSize; private final int mCardState; @@ -200,6 +207,8 @@ public final class Ndef extends BasicTagTechnology { return NFC_FORUM_TYPE_4; case TYPE_MIFARE_CLASSIC: return MIFARE_CLASSIC; + case TYPE_ICODE_SLI: + return ICODE_SLI; default: return UNKNOWN; } diff --git a/core/java/android/nfc/tech/package.html b/core/java/android/nfc/tech/package.html new file mode 100644 index 0000000..a99828f --- /dev/null +++ b/core/java/android/nfc/tech/package.html @@ -0,0 +1,13 @@ +<HTML> +<BODY> +<p> +These classes provide access to a tag technology's features, which vary by the type +of tag that is scanned. A scanned tag can support multiple technologies, and you can find +out what they are by calling {@link android.nfc.Tag#getTechList getTechList()}.</p> + +<p>For more information on dealing with tag technologies and handling the ones that you care about, see +<a href="{@docRoot}guide/topics/nfc/index.html#dispatch">The Tag Dispatch System</a>. +The {@link android.nfc.tech.TagTechnology} interface provides an overview of the +supported technologies.</p> +</BODY> +</HTML> diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java index e4485d1..4ff3572 100644 --- a/core/java/android/os/BatteryStats.java +++ b/core/java/android/os/BatteryStats.java @@ -132,6 +132,7 @@ public abstract class BatteryStats implements Parcelable { private static final String NETWORK_DATA = "nt"; private static final String USER_ACTIVITY_DATA = "ua"; private static final String BATTERY_DATA = "bt"; + private static final String BATTERY_DISCHARGE_DATA = "dc"; private static final String BATTERY_LEVEL_DATA = "lv"; private static final String WIFI_LOCK_DATA = "wfl"; private static final String MISC_DATA = "m"; @@ -801,6 +802,30 @@ public abstract class BatteryStats implements Parcelable { public abstract int getHighDischargeAmountSinceCharge(); /** + * Get the amount the battery has discharged while the screen was on, + * since the last time power was unplugged. + */ + public abstract int getDischargeAmountScreenOn(); + + /** + * Get the amount the battery has discharged while the screen was on, + * since the last time the device was charged. + */ + public abstract int getDischargeAmountScreenOnSinceCharge(); + + /** + * Get the amount the battery has discharged while the screen was off, + * since the last time power was unplugged. + */ + public abstract int getDischargeAmountScreenOff(); + + /** + * Get the amount the battery has discharged while the screen was off, + * since the last time the device was charged. + */ + public abstract int getDischargeAmountScreenOffSinceCharge(); + + /** * Returns the total, last, or current battery uptime in microseconds. * * @param curTime the elapsed realtime in microseconds. @@ -1095,6 +1120,17 @@ public abstract class BatteryStats implements Parcelable { getDischargeCurrentLevel()); } + if (which == STATS_SINCE_UNPLUGGED) { + dumpLine(pw, 0 /* uid */, category, BATTERY_DISCHARGE_DATA, + getDischargeStartLevel()-getDischargeCurrentLevel(), + getDischargeStartLevel()-getDischargeCurrentLevel(), + getDischargeAmountScreenOn(), getDischargeAmountScreenOff()); + } else { + dumpLine(pw, 0 /* uid */, category, BATTERY_DISCHARGE_DATA, + getLowDischargeAmountSinceCharge(), getHighDischargeAmountSinceCharge(), + getDischargeAmountScreenOn(), getDischargeAmountScreenOff()); + } + if (reqUid < 0) { Map<String, ? extends BatteryStats.Timer> kernelWakelocks = getKernelWakelockStats(); if (kernelWakelocks.size() > 0) { @@ -1451,6 +1487,10 @@ public abstract class BatteryStats implements Parcelable { pw.print(prefix); pw.print(" Last discharge cycle end level: "); pw.println(getDischargeCurrentLevel()); } + pw.print(prefix); pw.print(" Amount discharged while screen on: "); + pw.println(getDischargeAmountScreenOn()); + pw.print(prefix); pw.print(" Amount discharged while screen off: "); + pw.println(getDischargeAmountScreenOff()); pw.println(" "); } else { pw.print(prefix); pw.println(" Device battery use since last full charge"); @@ -1458,6 +1498,10 @@ public abstract class BatteryStats implements Parcelable { pw.println(getLowDischargeAmountSinceCharge()); pw.print(prefix); pw.print(" Amount discharged (upper bound): "); pw.println(getHighDischargeAmountSinceCharge()); + pw.print(prefix); pw.print(" Amount discharged while screen on: "); + pw.println(getDischargeAmountScreenOnSinceCharge()); + pw.print(prefix); pw.print(" Amount discharged while screen off: "); + pw.println(getDischargeAmountScreenOffSinceCharge()); pw.println(" "); } @@ -1847,7 +1891,6 @@ public abstract class BatteryStats implements Parcelable { final int NU = uidStats.size(); boolean didPid = false; long nowRealtime = SystemClock.elapsedRealtime(); - StringBuilder sb = new StringBuilder(64); for (int i=0; i<NU; i++) { Uid uid = uidStats.valueAt(i); SparseArray<? extends Uid.Pid> pids = uid.getPidStats(); diff --git a/core/java/android/os/Looper.java b/core/java/android/os/Looper.java index d360140..2a66616 100644 --- a/core/java/android/os/Looper.java +++ b/core/java/android/os/Looper.java @@ -17,6 +17,7 @@ package android.os; import android.util.Config; +import android.util.Log; import android.util.Printer; /** @@ -106,6 +107,12 @@ public class Looper { public static final void loop() { Looper me = myLooper(); MessageQueue queue = me.mQueue; + + // Make sure the identity of this thread is that of the local process, + // and keep track of what that identity token actually is. + Binder.clearCallingIdentity(); + final long ident = Binder.clearCallingIdentity(); + while (true) { Message msg = queue.next(); // might block //if (!me.mRun) { @@ -124,6 +131,18 @@ public class Looper { if (me.mLogging!= null) me.mLogging.println( "<<<<< Finished to " + msg.target + " " + msg.callback); + + // Make sure that during the course of dispatching the + // identity of the thread wasn't corrupted. + final long newIdent = Binder.clearCallingIdentity(); + if (ident != newIdent) { + Log.wtf("Looper", "Thread identity changed from 0x" + + Long.toHexString(ident) + " to 0x" + + Long.toHexString(newIdent) + " while dispatching to " + + msg.target.getClass().getName() + " " + + msg.callback + " what=" + msg.what); + } + msg.recycle(); } } diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java index a718fc6..e1fdfb6 100644 --- a/core/java/android/os/Process.java +++ b/core/java/android/os/Process.java @@ -89,7 +89,7 @@ public class Process { * Defines the UID/GID for the NFC service process. * @hide */ - public static final int NFC_UID = 1022; + public static final int NFC_UID = 1025; /** * Defines the start of a range of UIDs (and GIDs), going from this diff --git a/core/java/android/os/RecoverySystem.java b/core/java/android/os/RecoverySystem.java index 6d19f41..74b4fcb 100644 --- a/core/java/android/os/RecoverySystem.java +++ b/core/java/android/os/RecoverySystem.java @@ -16,6 +16,11 @@ package android.os; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.util.Log; + import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileNotFoundException; @@ -37,9 +42,6 @@ import java.util.List; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; -import android.content.Context; -import android.util.Log; - import org.apache.harmony.security.asn1.BerInputStream; import org.apache.harmony.security.pkcs7.ContentInfo; import org.apache.harmony.security.pkcs7.SignedData; @@ -336,8 +338,21 @@ public class RecoverySystem { * @throws IOException if writing the recovery command file * fails, or if the reboot itself fails. */ - public static void rebootWipeUserData(Context context) - throws IOException { + public static void rebootWipeUserData(Context context) throws IOException { + final ConditionVariable condition = new ConditionVariable(); + + Intent intent = new Intent("android.intent.action.MASTER_CLEAR_NOTIFICATION"); + context.sendOrderedBroadcast(intent, android.Manifest.permission.MASTER_CLEAR, + new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + condition.open(); + } + }, null, 0, null, null); + + // Block until the ordered broadcast has completed. + condition.block(); + bootCommand(context, "--wipe_data"); } diff --git a/core/java/android/pim/ICalendar.java b/core/java/android/pim/ICalendar.java index cc0f45e..9c4eaf4 100644 --- a/core/java/android/pim/ICalendar.java +++ b/core/java/android/pim/ICalendar.java @@ -578,6 +578,23 @@ public class ICalendar { + text); } parameter.name = text.substring(startIndex + 1, equalIndex); + } else if (c == '"') { + if (parameter == null) { + throw new FormatException("Expected parameter before '\"' in " + text); + } + if (equalIndex == -1) { + throw new FormatException("Expected '=' within parameter in " + text); + } + if (state.index > equalIndex + 1) { + throw new FormatException("Parameter value cannot contain a '\"' in " + text); + } + final int endQuote = text.indexOf('"', state.index + 1); + if (endQuote < 0) { + throw new FormatException("Expected closing '\"' in " + text); + } + parameter.value = text.substring(state.index + 1, endQuote); + state.index = endQuote + 1; + return parameter; } ++state.index; } diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 2a13d520..38c5dc4 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -1101,6 +1101,12 @@ public final class Settings { public static final String RADIO_CELL = "cell"; /** + * Constant for use in AIRPLANE_MODE_RADIOS to specify WiMAX radio. + * @hide + */ + public static final String RADIO_WIMAX = "wimax"; + + /** * A comma separated list of radios that need to be disabled when airplane mode * is on. This overrides WIFI_ON and BLUETOOTH_ON, if Wi-Fi and bluetooth are * included in the comma separated list. @@ -2559,6 +2565,14 @@ public final class Settings { "wifi_networks_available_repeat_delay"; /** + * Whether to nofity the user of WiMAX network. + * If WiMAX is connected or disconnected, we will put this notification up. + * @hide + */ + public static final String WIMAX_NETWORKS_AVAILABLE_NOTIFICATION_ON = + "wimax_networks_available_notification_on"; + + /** * The number of radio channels that are allowed in the local * 802.11 regulatory domain. * @hide @@ -2695,6 +2709,12 @@ public final class Settings { "wifi_mobile_data_transition_wakelock_timeout_ms"; /** + * Whether the Wimax should be on. Only the WiMAX service should touch this. + * @hide + */ + public static final String WIMAX_ON = "wimax_on"; + + /** * Whether background data usage is allowed by the user. See * ConnectivityManager for more info. */ diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java index 1a35112..62f66b6 100755 --- a/core/java/android/provider/Telephony.java +++ b/core/java/android/provider/Telephony.java @@ -1723,6 +1723,21 @@ public final class Telephony { public static final String TYPE = "type"; + /** + * The protocol to be used to connect to this APN. + * + * One of the PDP_type values in TS 27.007 section 10.1.1. + * For example, "IP", "IPV6", "IPV4V6", or "PPP". + */ + public static final String PROTOCOL = "protocol"; + + /** + * The protocol to be used to connect to this APN when roaming. + * + * The syntax is the same as protocol. + */ + public static final String ROAMING_PROTOCOL = "roaming_protocol"; + public static final String CURRENT = "current"; } diff --git a/core/java/android/speech/RecognitionService.java b/core/java/android/speech/RecognitionService.java index 75a5ed5..32b2d8f 100644 --- a/core/java/android/speech/RecognitionService.java +++ b/core/java/android/speech/RecognitionService.java @@ -68,6 +68,8 @@ public abstract class RecognitionService extends Service { private static final int MSG_CANCEL = 3; + private static final int MSG_RESET = 4; + private final Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { @@ -81,6 +83,10 @@ public abstract class RecognitionService extends Service { break; case MSG_CANCEL: dispatchCancel((IRecognitionListener) msg.obj); + break; + case MSG_RESET: + dispatchClearCallback(); + break; } } }; @@ -128,6 +134,10 @@ public abstract class RecognitionService extends Service { } } + private void dispatchClearCallback() { + mCurrentCallback = null; + } + private class StartListeningArgs { public final Intent mIntent; @@ -241,7 +251,7 @@ public abstract class RecognitionService extends Service { * @param error code is defined in {@link SpeechRecognizer} */ public void error(int error) throws RemoteException { - mCurrentCallback = null; + Message.obtain(mHandler, MSG_RESET).sendToTarget(); mListener.onError(error); } @@ -278,7 +288,7 @@ public abstract class RecognitionService extends Service { * {@link SpeechRecognizer#RESULTS_RECOGNITION} as a parameter */ public void results(Bundle results) throws RemoteException { - mCurrentCallback = null; + Message.obtain(mHandler, MSG_RESET).sendToTarget(); mListener.onResults(results); } diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java index 4e197cd..b62aa40 100755 --- a/core/java/android/text/Layout.java +++ b/core/java/android/text/Layout.java @@ -1154,7 +1154,7 @@ public abstract class Layout { if (h2 < 0.5f) h2 = 0.5f; - if (h1 == h2) { + if (Float.compare(h1, h2) == 0) { dest.moveTo(h1, top); dest.lineTo(h1, bottom); } else { diff --git a/core/java/android/text/TextUtils.java b/core/java/android/text/TextUtils.java index 8675d05..0d7aa02 100644 --- a/core/java/android/text/TextUtils.java +++ b/core/java/android/text/TextUtils.java @@ -627,10 +627,16 @@ public class TextUtils { public CharSequence createFromParcel(Parcel p) { int kind = p.readInt(); - if (kind == 1) - return p.readString(); + String string = p.readString(); + if (string == null) { + return null; + } + + if (kind == 1) { + return string; + } - SpannableString sp = new SpannableString(p.readString()); + SpannableString sp = new SpannableString(string); while (true) { kind = p.readInt(); diff --git a/core/java/android/text/method/MultiTapKeyListener.java b/core/java/android/text/method/MultiTapKeyListener.java index 6d94788..2a739fa 100644 --- a/core/java/android/text/method/MultiTapKeyListener.java +++ b/core/java/android/text/method/MultiTapKeyListener.java @@ -116,7 +116,7 @@ public class MultiTapKeyListener extends BaseKeyListener content.replace(selStart, selEnd, String.valueOf(current).toUpperCase()); removeTimeouts(content); - Timeout t = new Timeout(content); + new Timeout(content); // for its side effects return true; } @@ -124,7 +124,7 @@ public class MultiTapKeyListener extends BaseKeyListener content.replace(selStart, selEnd, String.valueOf(current).toLowerCase()); removeTimeouts(content); - Timeout t = new Timeout(content); + new Timeout(content); // for its side effects return true; } @@ -140,7 +140,7 @@ public class MultiTapKeyListener extends BaseKeyListener content.replace(selStart, selEnd, val, ix, ix + 1); removeTimeouts(content); - Timeout t = new Timeout(content); + new Timeout(content); // for its side effects return true; } @@ -206,7 +206,7 @@ public class MultiTapKeyListener extends BaseKeyListener } removeTimeouts(content); - Timeout t = new Timeout(content); + new Timeout(content); // for its side effects // Set up the callback so we can remove the timeout if the // cursor moves. diff --git a/core/java/android/text/style/DrawableMarginSpan.java b/core/java/android/text/style/DrawableMarginSpan.java index 3c471a5..c2564d5 100644 --- a/core/java/android/text/style/DrawableMarginSpan.java +++ b/core/java/android/text/style/DrawableMarginSpan.java @@ -50,9 +50,6 @@ implements LeadingMarginSpan, LineHeightSpan int dw = mDrawable.getIntrinsicWidth(); int dh = mDrawable.getIntrinsicHeight(); - if (dir < 0) - x -= dw; - // XXX What to do about Paint? mDrawable.setBounds(ix, itop, ix+dw, itop+dh); mDrawable.draw(c); diff --git a/core/java/android/util/SparseArray.java b/core/java/android/util/SparseArray.java index 1c8b330..4c13460 100644 --- a/core/java/android/util/SparseArray.java +++ b/core/java/android/util/SparseArray.java @@ -90,6 +90,17 @@ public class SparseArray<E> { delete(key); } + /** + * Removes the mapping at the specified index. + * @hide + */ + public void removeAt(int index) { + if (mValues[index] != DELETED) { + mValues[index] = DELETED; + mGarbage = true; + } + } + private void gc() { // Log.e("SparseArray", "gc start with " + mSize); diff --git a/core/java/android/view/GestureDetector.java b/core/java/android/view/GestureDetector.java index c1e1049..79b3d42 100644..100755 --- a/core/java/android/view/GestureDetector.java +++ b/core/java/android/view/GestureDetector.java @@ -193,10 +193,8 @@ public class GestureDetector { } } - // TODO: ViewConfiguration - private int mBiggerTouchSlopSquare = 20 * 20; - private int mTouchSlopSquare; + private int mLargeTouchSlopSquare; private int mDoubleTapSlopSquare; private int mMinimumFlingVelocity; private int mMaximumFlingVelocity; @@ -384,10 +382,11 @@ public class GestureDetector { mIgnoreMultitouch = ignoreMultitouch; // Fallback to support pre-donuts releases - int touchSlop, doubleTapSlop; + int touchSlop, largeTouchSlop, doubleTapSlop; if (context == null) { //noinspection deprecation touchSlop = ViewConfiguration.getTouchSlop(); + largeTouchSlop = touchSlop + 2; doubleTapSlop = ViewConfiguration.getDoubleTapSlop(); //noinspection deprecation mMinimumFlingVelocity = ViewConfiguration.getMinimumFlingVelocity(); @@ -395,11 +394,13 @@ public class GestureDetector { } else { final ViewConfiguration configuration = ViewConfiguration.get(context); touchSlop = configuration.getScaledTouchSlop(); + largeTouchSlop = configuration.getScaledLargeTouchSlop(); doubleTapSlop = configuration.getScaledDoubleTapSlop(); mMinimumFlingVelocity = configuration.getScaledMinimumFlingVelocity(); mMaximumFlingVelocity = configuration.getScaledMaximumFlingVelocity(); } mTouchSlopSquare = touchSlop * touchSlop; + mLargeTouchSlopSquare = largeTouchSlop * largeTouchSlop; mDoubleTapSlopSquare = doubleTapSlop * doubleTapSlop; } @@ -534,7 +535,7 @@ public class GestureDetector { mHandler.removeMessages(SHOW_PRESS); mHandler.removeMessages(LONG_PRESS); } - if (distance > mBiggerTouchSlopSquare) { + if (distance > mLargeTouchSlopSquare) { mAlwaysInBiggerTapRegion = false; } } else if ((Math.abs(scrollX) >= 1) || (Math.abs(scrollY) >= 1)) { diff --git a/core/java/android/view/ViewConfiguration.java b/core/java/android/view/ViewConfiguration.java index 924c9d4..5397449 100644..100755 --- a/core/java/android/view/ViewConfiguration.java +++ b/core/java/android/view/ViewConfiguration.java @@ -102,6 +102,12 @@ public class ViewConfiguration { private static final int TOUCH_SLOP = 16; /** + * Distance a touch can wander before we think the user is the first touch + * in a sequence of double tap + */ + private static final int LARGE_TOUCH_SLOP = 18; + + /** * Distance a touch can wander before we think the user is attempting a paged scroll * (in dips) */ @@ -156,6 +162,7 @@ public class ViewConfiguration { private final int mMaximumFlingVelocity; private final int mScrollbarSize; private final int mTouchSlop; + private final int mLargeTouchSlop; private final int mPagingTouchSlop; private final int mDoubleTapSlop; private final int mWindowTouchSlop; @@ -177,6 +184,7 @@ public class ViewConfiguration { mMaximumFlingVelocity = MAXIMUM_FLING_VELOCITY; mScrollbarSize = SCROLL_BAR_SIZE; mTouchSlop = TOUCH_SLOP; + mLargeTouchSlop = LARGE_TOUCH_SLOP; mPagingTouchSlop = PAGING_TOUCH_SLOP; mDoubleTapSlop = DOUBLE_TAP_SLOP; mWindowTouchSlop = WINDOW_TOUCH_SLOP; @@ -206,6 +214,7 @@ public class ViewConfiguration { mMaximumFlingVelocity = (int) (density * MAXIMUM_FLING_VELOCITY + 0.5f); mScrollbarSize = (int) (density * SCROLL_BAR_SIZE + 0.5f); mTouchSlop = (int) (density * TOUCH_SLOP + 0.5f); + mLargeTouchSlop = (int) (density * LARGE_TOUCH_SLOP + 0.5f); mPagingTouchSlop = (int) (density * PAGING_TOUCH_SLOP + 0.5f); mDoubleTapSlop = (int) (density * DOUBLE_TAP_SLOP + 0.5f); mWindowTouchSlop = (int) (density * WINDOW_TOUCH_SLOP + 0.5f); @@ -367,6 +376,14 @@ public class ViewConfiguration { } /** + * @return Distance a touch can wander before we think the user is the first touch + * in a sequence of double tap + */ + public int getScaledLargeTouchSlop() { + return mLargeTouchSlop; + } + + /** * @return Distance a touch can wander before we think the user is scrolling a full page * in dips */ diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/ViewRoot.java index ccaef40..afec1c3 100644 --- a/core/java/android/view/ViewRoot.java +++ b/core/java/android/view/ViewRoot.java @@ -176,6 +176,7 @@ public final class ViewRoot extends Handler implements ViewParent, private final Surface mSurface = new Surface(); boolean mAdded; + private boolean mAttached; boolean mAddedTouchMode; /*package*/ int mAddNesting; @@ -762,7 +763,10 @@ public final class ViewRoot extends Handler implements ViewParent, attachInfo.mKeepScreenOn = false; viewVisibilityChanged = false; mLastConfiguration.setTo(host.getResources().getConfiguration()); - host.dispatchAttachedToWindow(attachInfo, 0); + if (!mAttached) { + host.dispatchAttachedToWindow(attachInfo, 0); + mAttached = true; + } //Log.i(TAG, "Screen on initialized: " + attachInfo.mKeepScreenOn); } else { @@ -1743,8 +1747,9 @@ public final class ViewRoot extends Handler implements ViewParent, void dispatchDetachedFromWindow() { if (Config.LOGV) Log.v(TAG, "Detaching in " + this + " of " + mSurface); - if (mView != null) { + if (mView != null && mAttached) { mView.dispatchDetachedFromWindow(); + mAttached = false; } mView = null; diff --git a/core/java/android/webkit/FrameLoader.java b/core/java/android/webkit/FrameLoader.java index 021b53c..4bc12d4 100644 --- a/core/java/android/webkit/FrameLoader.java +++ b/core/java/android/webkit/FrameLoader.java @@ -186,7 +186,8 @@ class FrameLoader { settings.getAllowFileAccess())).sendToTarget(); } return true; - } else if (URLUtil.isContentUrl(url)) { + } else if (settings.getAllowContentAccess() && + URLUtil.isContentUrl(url)) { // Send the raw url to the ContentLoader because it will do a // permission check and the url has to match. if (loadListener.isSynchronous()) { diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java index 4d70e7c..89e25e8 100644 --- a/core/java/android/webkit/WebSettings.java +++ b/core/java/android/webkit/WebSettings.java @@ -206,6 +206,7 @@ public class WebSettings { private boolean mSupportZoom = true; private boolean mBuiltInZoomControls = false; private boolean mAllowFileAccess = true; + private boolean mAllowContentAccess = true; private boolean mLoadWithOverviewMode = false; private boolean mUseWebViewBackgroundOverscrollBackground = true; @@ -458,7 +459,9 @@ public class WebSettings { /** * Enable or disable file access within WebView. File access is enabled by - * default. + * default. Note that this enables or disables file system access only. + * Assets and resources are still accessible using file:///android_asset and + * file:///android_res. */ public void setAllowFileAccess(boolean allow) { mAllowFileAccess = allow; @@ -472,6 +475,24 @@ public class WebSettings { } /** + * Enable or disable content url access within WebView. Content url access + * allows WebView to load content from a content provider installed in the + * system. The default is enabled. + * @hide + */ + public void setAllowContentAccess(boolean allow) { + mAllowContentAccess = allow; + } + + /** + * Returns true if this WebView supports content url access. + * @hide + */ + public boolean getAllowContentAccess() { + return mAllowContentAccess; + } + + /** * Set whether the WebView loads a page with overview mode. */ public void setLoadWithOverviewMode(boolean overview) { diff --git a/core/java/android/widget/DatePicker.java b/core/java/android/widget/DatePicker.java index a371290..595b487 100644 --- a/core/java/android/widget/DatePicker.java +++ b/core/java/android/widget/DatePicker.java @@ -22,6 +22,7 @@ import android.content.res.TypedArray; import android.os.Parcel; import android.os.Parcelable; import android.text.format.DateFormat; +import android.text.format.DateUtils; import android.util.AttributeSet; import android.util.SparseArray; import android.view.LayoutInflater; @@ -33,6 +34,7 @@ import com.android.internal.R; import java.text.DateFormatSymbols; import java.text.SimpleDateFormat; import java.util.Calendar; +import java.util.Locale; /** * A view for selecting a month / year / day based on a calendar like layout. @@ -47,7 +49,10 @@ public class DatePicker extends FrameLayout { private static final int DEFAULT_START_YEAR = 1900; private static final int DEFAULT_END_YEAR = 2100; - + + // This ignores Undecimber, but we only support real Gregorian calendars. + private static final int NUMBER_OF_MONTHS = 12; + /* UI Components */ private final NumberPicker mDayPicker; private final NumberPicker mMonthPicker; @@ -62,6 +67,10 @@ public class DatePicker extends FrameLayout { private int mMonth; private int mYear; + private Object mMonthUpdateLock = new Object(); + private volatile Locale mMonthLocale; + private String[] mShortMonths; + /** * The callback used to indicate the user changes the date. */ @@ -102,8 +111,7 @@ public class DatePicker extends FrameLayout { }); mMonthPicker = (NumberPicker) findViewById(R.id.month); mMonthPicker.setFormatter(NumberPicker.TWO_DIGIT_FORMATTER); - DateFormatSymbols dfs = new DateFormatSymbols(); - String[] months = dfs.getShortMonths(); + final String[] months = getShortMonths(); /* * If the user is in a locale where the month names are numeric, @@ -114,9 +122,9 @@ public class DatePicker extends FrameLayout { for (int i = 0; i < months.length; i++) { months[i] = String.valueOf(i + 1); } - mMonthPicker.setRange(1, 12); + mMonthPicker.setRange(1, NUMBER_OF_MONTHS); } else { - mMonthPicker.setRange(1, 12, months); + mMonthPicker.setRange(1, NUMBER_OF_MONTHS, months); } mMonthPicker.setSpeed(200); @@ -246,11 +254,30 @@ public class DatePicker extends FrameLayout { mMonth = monthOfYear; mDay = dayOfMonth; updateSpinners(); - reorderPickers(new DateFormatSymbols().getShortMonths()); + reorderPickers(getShortMonths()); notifyDateChanged(); } } + private String[] getShortMonths() { + final Locale currentLocale = Locale.getDefault(); + if (currentLocale.equals(mMonthLocale) && mShortMonths != null) { + return mShortMonths; + } else { + synchronized (mMonthUpdateLock) { + if (!currentLocale.equals(mMonthLocale)) { + mShortMonths = new String[NUMBER_OF_MONTHS]; + for (int i = 0; i < NUMBER_OF_MONTHS; i++) { + mShortMonths[i] = DateUtils.getMonthString(Calendar.JANUARY + i, + DateUtils.LENGTH_MEDIUM); + } + mMonthLocale = currentLocale; + } + } + return mShortMonths; + } + } + private static class SavedState extends BaseSavedState { private final int mYear; diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java index 76755de..66524ab 100644 --- a/core/java/android/widget/PopupWindow.java +++ b/core/java/android/widget/PopupWindow.java @@ -1421,6 +1421,10 @@ public class PopupWindow { @Override public boolean dispatchKeyEvent(KeyEvent event) { if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) { + if (getKeyDispatcherState() == null) { + return super.dispatchKeyEvent(event); + } + if (event.getAction() == KeyEvent.ACTION_DOWN && event.getRepeatCount() == 0) { getKeyDispatcherState().startTracking(event, this); diff --git a/core/java/android/widget/ScrollView.java b/core/java/android/widget/ScrollView.java index de38d05..b1e1fbc 100644 --- a/core/java/android/widget/ScrollView.java +++ b/core/java/android/widget/ScrollView.java @@ -832,7 +832,7 @@ public class ScrollView extends FrameLayout { int count = getChildCount(); if (count > 0) { View view = getChildAt(count - 1); - mTempRect.bottom = view.getBottom(); + mTempRect.bottom = view.getBottom() + mPaddingBottom; mTempRect.top = mTempRect.bottom - height; } } @@ -912,9 +912,7 @@ public class ScrollView extends FrameLayout { } else if (direction == View.FOCUS_DOWN) { if (getChildCount() > 0) { int daBottom = getChildAt(0).getBottom(); - - int screenBottom = getScrollY() + getHeight(); - + int screenBottom = getScrollY() + getHeight() - mPaddingBottom; if (daBottom - screenBottom < maxJump) { scrollDelta = daBottom - screenBottom; } diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 97b05af..68600cf 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -301,6 +301,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener // Set when this TextView gained focus with some text selected. Will start selection mode. private boolean mCreatedWithASelection = false; + private boolean mNoContextMenuOnUp = false; + /* * Kick-start the font cache for the zygote process (to pay the cost of * initializing freetype for our default font only once). @@ -6798,8 +6800,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener // Restore previous selection Selection.setSelection((Spannable)mText, prevStart, prevEnd); - // Tapping inside the selection displays the cut/copy/paste context menu - showContextMenu(); + // Tapping inside the selection displays the cut/copy/paste context menu, unless + // this is a double tap that should simply trigger text selection mode. + if (!mNoContextMenuOnUp) showContextMenu(); } else { // Tapping outside stops selection mode, if any stopTextSelectionMode(); @@ -6857,7 +6860,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener mScrolled = false; } - final boolean superResult = super.onTouchEvent(event); + boolean result = super.onTouchEvent(event); /* * Don't handle the release after a long press, because it will @@ -6866,11 +6869,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener */ if (mEatTouchRelease && action == MotionEvent.ACTION_UP) { mEatTouchRelease = false; - return superResult; - } - - if ((mMovement != null || onCheckIsTextEditor()) && isEnabled() && - mText instanceof Spannable && mLayout != null) { + } else if ((mMovement != null || onCheckIsTextEditor()) && mText instanceof Spannable && + mLayout != null) { boolean handled = false; // Save previous selection, in case this event is used to show the IME. @@ -6911,12 +6911,14 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } } - if (handled) { - return true; - } + if (handled) result = true; + } + + if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) { + mNoContextMenuOnUp = false; } - return superResult; + return result; } private void prepareCursorControllers() { @@ -8200,9 +8202,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener final int slopSquared = doubleTapSlop * doubleTapSlop; if (distanceSquared < slopSquared) { startTextSelectionMode(); - // Hacky: onTapUpEvent will open a context menu with cut/copy - // Prevent this by hiding handles which will be revived instead. - hide(); + // prevents onTapUpEvent from opening a context menu with cut/copy + mNoContextMenuOnUp = true; } } mPreviousTapPositionX = x; |