diff options
99 files changed, 3278 insertions, 1372 deletions
@@ -120,6 +120,7 @@ LOCAL_SRC_FILES += \ core/java/android/os/IMessenger.aidl \ core/java/android/os/storage/IMountService.aidl \ core/java/android/os/storage/IMountServiceListener.aidl \ + core/java/android/os/storage/IMountShutdownObserver.aidl \ core/java/android/os/INetworkManagementService.aidl \ core/java/android/os/INetStatService.aidl \ core/java/android/os/IPermissionController.aidl \ diff --git a/api/current.xml b/api/current.xml index dd0a804..c2d273d 100644 --- a/api/current.xml +++ b/api/current.xml @@ -45243,209 +45243,209 @@ visibility="public" > </field> -<field name="FEATURE_CAMERA" +<field name="FEATURE_BLUETOOTH" type="java.lang.String" transient="false" volatile="false" - value=""android.hardware.camera"" + value=""android.hardware.bluetooth"" static="true" final="true" deprecated="not deprecated" visibility="public" > </field> -<field name="FEATURE_CAMERA_AUTOFOCUS" +<field name="FEATURE_CAMERA" type="java.lang.String" transient="false" volatile="false" - value=""android.hardware.camera.autofocus"" + value=""android.hardware.camera"" static="true" final="true" deprecated="not deprecated" visibility="public" > </field> -<field name="FEATURE_CAMERA_FLASH" +<field name="FEATURE_CAMERA_AUTOFOCUS" type="java.lang.String" transient="false" volatile="false" - value=""android.hardware.camera.flash"" + value=""android.hardware.camera.autofocus"" static="true" final="true" deprecated="not deprecated" visibility="public" > </field> -<field name="FEATURE_LIVE_WALLPAPER" +<field name="FEATURE_CAMERA_FLASH" type="java.lang.String" transient="false" volatile="false" - value=""android.software.live_wallpaper"" + value=""android.hardware.camera.flash"" static="true" final="true" deprecated="not deprecated" visibility="public" > </field> -<field name="FEATURE_SENSOR_LIGHT" +<field name="FEATURE_LIVE_WALLPAPER" type="java.lang.String" transient="false" volatile="false" - value=""android.hardware.sensor.light"" + value=""android.software.live_wallpaper"" static="true" final="true" deprecated="not deprecated" visibility="public" > </field> -<field name="FEATURE_SENSOR_PROXIMITY" +<field name="FEATURE_LOCATION" type="java.lang.String" transient="false" volatile="false" - value=""android.hardware.sensor.proximity"" + value=""android.hardware.location"" static="true" final="true" deprecated="not deprecated" visibility="public" > </field> -<field name="FEATURE_TELEPHONY" +<field name="FEATURE_LOCATION_GPS" type="java.lang.String" transient="false" volatile="false" - value=""android.hardware.telephony"" + value=""android.hardware.location.gps"" static="true" final="true" deprecated="not deprecated" visibility="public" > </field> -<field name="FEATURE_TELEPHONY_CDMA" +<field name="FEATURE_LOCATION_NETWORK" type="java.lang.String" transient="false" volatile="false" - value=""android.hardware.telephony.cdma"" + value=""android.hardware.location.network"" static="true" final="true" deprecated="not deprecated" visibility="public" > </field> -<field name="FEATURE_TELEPHONY_GSM" +<field name="FEATURE_MICROPHONE" type="java.lang.String" transient="false" volatile="false" - value=""android.hardware.telephony.gsm"" + value=""android.hardware.microphone"" static="true" final="true" deprecated="not deprecated" visibility="public" > </field> -<field name="FEATURE_TOUCHSCREEN_MULTITOUCH" +<field name="FEATURE_SENSOR_ACCELEROMETER" type="java.lang.String" transient="false" volatile="false" - value=""android.hardware.touchscreen.multitouch"" + value=""android.hardware.sensor.accelerometer"" static="true" final="true" deprecated="not deprecated" visibility="public" > </field> -<field name="FEATURE_TOUCHSCREEN_MULTITOUCH_DISTINCT" +<field name="FEATURE_SENSOR_COMPASS" type="java.lang.String" transient="false" volatile="false" - value=""android.hardware.touchscreen.multitouch.distinct"" + value=""android.hardware.sensor.compass"" static="true" final="true" deprecated="not deprecated" visibility="public" > </field> -<field name="FEATURE_BLUETOOTH" +<field name="FEATURE_SENSOR_LIGHT" type="java.lang.String" transient="false" volatile="false" - value=""android.hardware.bluetooth"" + value=""android.hardware.sensor.light"" static="true" final="true" deprecated="not deprecated" visibility="public" > </field> -<field name="FEATURE_LOCATION" +<field name="FEATURE_SENSOR_PROXIMITY" type="java.lang.String" transient="false" volatile="false" - value=""android.hardware.location"" + value=""android.hardware.sensor.proximity"" static="true" final="true" deprecated="not deprecated" visibility="public" > </field> -<field name="FEATURE_LOCATION_GPS" +<field name="FEATURE_TELEPHONY" type="java.lang.String" transient="false" volatile="false" - value=""android.hardware.location.gps"" + value=""android.hardware.telephony"" static="true" final="true" deprecated="not deprecated" visibility="public" > </field> -<field name="FEATURE_LOCATION_NETWORK" +<field name="FEATURE_TELEPHONY_CDMA" type="java.lang.String" transient="false" volatile="false" - value=""android.hardware.location.network"" + value=""android.hardware.telephony.cdma"" static="true" final="true" deprecated="not deprecated" visibility="public" > </field> -<field name="FEATURE_MICROPHONE" +<field name="FEATURE_TELEPHONY_GSM" type="java.lang.String" transient="false" volatile="false" - value=""android.hardware.microphone"" + value=""android.hardware.telephony.gsm"" static="true" final="true" deprecated="not deprecated" visibility="public" > </field> -<field name="FEATURE_WIFI" +<field name="FEATURE_TOUCHSCREEN_MULTITOUCH" type="java.lang.String" transient="false" volatile="false" - value=""android.hardware.wifi"" + value=""android.hardware.touchscreen.multitouch"" static="true" final="true" deprecated="not deprecated" visibility="public" > </field> -<field name="FEATURE_SENSOR_ACCELEROMETER" +<field name="FEATURE_TOUCHSCREEN_MULTITOUCH_DISTINCT" type="java.lang.String" transient="false" volatile="false" - value=""android.hardware.sensor.accelerometer"" + value=""android.hardware.touchscreen.multitouch.distinct"" static="true" final="true" deprecated="not deprecated" visibility="public" > </field> -<field name="FEATURE_SENSOR_COMPASS" +<field name="FEATURE_WIFI" type="java.lang.String" transient="false" volatile="false" - value=""android.hardware.sensor.compass"" + value=""android.hardware.wifi"" static="true" final="true" deprecated="not deprecated" @@ -71563,6 +71563,19 @@ deprecated="not deprecated" visibility="public" > +<method name="addCallbackBuffer" + return="void" + abstract="false" + native="true" + synchronized="false" + static="false" + final="true" + deprecated="not deprecated" + visibility="public" +> +<parameter name="callbackBuffer" type="byte[]"> +</parameter> +</method> <method name="autoFocus" return="void" abstract="false" @@ -71709,6 +71722,19 @@ <parameter name="cb" type="android.hardware.Camera.PreviewCallback"> </parameter> </method> +<method name="setPreviewCallbackWithBuffer" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="true" + deprecated="not deprecated" + visibility="public" +> +<parameter name="cb" type="android.hardware.Camera.PreviewCallback"> +</parameter> +</method> <method name="setPreviewDisplay" return="void" abstract="false" @@ -80262,6 +80288,19 @@ deprecated="not deprecated" visibility="public" > +<method name="abandonAudioFocus" + return="int" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="l" type="android.media.AudioManager.OnAudioFocusChangeListener"> +</parameter> +</method> <method name="adjustStreamVolume" return="void" abstract="false" @@ -80503,6 +80542,36 @@ <parameter name="volume" type="float"> </parameter> </method> +<method name="registerAudioFocusListener" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="l" type="android.media.AudioManager.OnAudioFocusChangeListener"> +</parameter> +</method> +<method name="requestAudioFocus" + return="int" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="l" type="android.media.AudioManager.OnAudioFocusChangeListener"> +</parameter> +<parameter name="streamType" type="int"> +</parameter> +<parameter name="durationHint" type="int"> +</parameter> +</method> <method name="setBluetoothA2dpOn" return="void" abstract="false" @@ -80710,6 +80779,19 @@ visibility="public" > </method> +<method name="unregisterAudioFocusListener" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="l" type="android.media.AudioManager.OnAudioFocusChangeListener"> +</parameter> +</method> <field name="ACTION_AUDIO_BECOMING_NOISY" type="java.lang.String" transient="false" @@ -80754,6 +80836,72 @@ visibility="public" > </field> +<field name="AUDIOFOCUS_GAIN" + type="int" + transient="false" + volatile="false" + value="1" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="AUDIOFOCUS_GAIN_TRANSIENT" + type="int" + transient="false" + volatile="false" + value="2" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="AUDIOFOCUS_LOSS" + type="int" + transient="false" + volatile="false" + value="-1" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="AUDIOFOCUS_LOSS_TRANSIENT" + type="int" + transient="false" + volatile="false" + value="-2" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="AUDIOFOCUS_REQUEST_FAILED" + type="int" + transient="false" + volatile="false" + value="0" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="AUDIOFOCUS_REQUEST_GRANTED" + type="int" + transient="false" + volatile="false" + value="1" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="EXTRA_RINGER_MODE" type="java.lang.String" transient="false" @@ -81283,6 +81431,27 @@ > </field> </class> +<interface name="AudioManager.OnAudioFocusChangeListener" + abstract="true" + static="true" + final="false" + deprecated="not deprecated" + visibility="public" +> +<method name="onAudioFocusChanged" + return="void" + abstract="true" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="focusChange" type="int"> +</parameter> +</method> +</interface> <class name="AudioRecord" extends="java.lang.Object" abstract="false" @@ -174432,6 +174601,30 @@ visibility="public" > </method> +<method name="getXVelocity" + return="float" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="id" type="int"> +</parameter> +</method> +<method name="getYVelocity" + return="float" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> <method name="getYVelocity" return="float" abstract="false" @@ -174442,6 +174635,8 @@ deprecated="not deprecated" visibility="public" > +<parameter name="id" type="int"> +</parameter> </method> <method name="obtain" return="android.view.VelocityTracker" @@ -179221,6 +179416,17 @@ visibility="public" > </method> +<method name="getScaledPagingTouchSlop" + return="int" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> <method name="getScaledScrollBarSize" return="int" abstract="false" @@ -193281,6 +193487,17 @@ visibility="public" > </method> +<method name="getUseSystemOverscrollBackground" + return="boolean" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> <method name="getUseWideViewPort" return="boolean" abstract="false" @@ -193860,6 +194077,19 @@ <parameter name="use" type="boolean"> </parameter> </method> +<method name="setUseSystemOverscrollBackground" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="system" type="boolean"> +</parameter> +</method> <method name="setUseWideViewPort" return="void" abstract="false" diff --git a/cmds/servicemanager/Android.mk b/cmds/servicemanager/Android.mk index 25266a2..fe5929b 100644 --- a/cmds/servicemanager/Android.mk +++ b/cmds/servicemanager/Android.mk @@ -10,5 +10,8 @@ include $(CLEAR_VARS) LOCAL_SHARED_LIBRARIES := liblog LOCAL_SRC_FILES := service_manager.c binder.c LOCAL_MODULE := servicemanager +ifeq ($(BOARD_USE_LVMX),true) + LOCAL_CFLAGS += -DLVMX +endif include $(BUILD_EXECUTABLE) endif diff --git a/cmds/servicemanager/service_manager.c b/cmds/servicemanager/service_manager.c index f3a4713..a2006c1 100644 --- a/cmds/servicemanager/service_manager.c +++ b/cmds/servicemanager/service_manager.c @@ -27,6 +27,9 @@ static struct { unsigned uid; const char *name; } allowed[] = { +#ifdef LVMX + { AID_MEDIA, "com.lifevibes.mx.ipc" }, +#endif { AID_MEDIA, "media.audio_flinger" }, { AID_MEDIA, "media.player" }, { AID_MEDIA, "media.camera" }, diff --git a/common/java/com/android/common/speech/LoggingEvents.java b/common/java/com/android/common/speech/LoggingEvents.java index 3b3ecb8..1f3c6ef 100644 --- a/common/java/com/android/common/speech/LoggingEvents.java +++ b/common/java/com/android/common/speech/LoggingEvents.java @@ -117,6 +117,12 @@ public class LoggingEvents { public static final String EXTRA_N_BEST_CHOOSE_INDEX = "index"; // value should be int public static final int TEXT_MODIFIED = 17; + public static final String EXTRA_TEXT_MODIFIED_LENGTH = "length"; // value should be int + public static final String EXTRA_TEXT_MODIFIED_TYPE = "type"; // value should be int below + public static final int TEXT_MODIFIED_TYPE_CHOOSE_SUGGESTION = 1; + public static final int TEXT_MODIFIED_TYPE_TYPING_DELETION = 2; + public static final int TEXT_MODIFIED_TYPE_TYPING_INSERTION = 3; + public static final int TEXT_MODIFIED_TYPE_TYPING_INSERTION_PUNCTUATION = 4; public static final int INPUT_ENDED = 18; diff --git a/core/java/android/accounts/AbstractAccountAuthenticator.java b/core/java/android/accounts/AbstractAccountAuthenticator.java index 8bc7428..c0c4c17 100644 --- a/core/java/android/accounts/AbstractAccountAuthenticator.java +++ b/core/java/android/accounts/AbstractAccountAuthenticator.java @@ -24,6 +24,10 @@ import android.content.pm.PackageManager; import android.content.Context; import android.content.Intent; import android.Manifest; +import android.text.TextUtils; +import android.util.Log; + +import java.util.Arrays; /** * Abstract base class for creating AccountAuthenticators. @@ -103,6 +107,8 @@ import android.Manifest; * writing activities to handle these requests. */ public abstract class AbstractAccountAuthenticator { + private static final String TAG = "AccountAuthenticator"; + private final Context mContext; public AbstractAccountAuthenticator(Context context) { @@ -111,19 +117,34 @@ public abstract class AbstractAccountAuthenticator { private class Transport extends IAccountAuthenticator.Stub { public void addAccount(IAccountAuthenticatorResponse response, String accountType, - String authTokenType, String[] requiredFeatures, Bundle options) + String authTokenType, String[] features, Bundle options) throws RemoteException { + if (Log.isLoggable(TAG, Log.VERBOSE)) { + Log.v(TAG, "addAccount: accountType " + accountType + + ", authTokenType " + authTokenType + + ", features " + (features == null ? "[]" : Arrays.toString(features))); + } checkBinderPermission(); try { final Bundle result = AbstractAccountAuthenticator.this.addAccount( new AccountAuthenticatorResponse(response), - accountType, authTokenType, requiredFeatures, options); + accountType, authTokenType, features, options); + if (Log.isLoggable(TAG, Log.VERBOSE)) { + result.keySet(); // force it to be unparcelled + Log.v(TAG, "addAccount: result " + AccountManager.sanitizeResult(result)); + } if (result != null) { response.onResult(result); } } catch (NetworkErrorException e) { + if (Log.isLoggable(TAG, Log.VERBOSE)) { + Log.v(TAG, "addAccount", e); + } response.onError(AccountManager.ERROR_CODE_NETWORK_ERROR, e.getMessage()); } catch (UnsupportedOperationException e) { + if (Log.isLoggable(TAG, Log.VERBOSE)) { + Log.v(TAG, "addAccount", e); + } response.onError(AccountManager.ERROR_CODE_UNSUPPORTED_OPERATION, "addAccount not supported"); } @@ -131,16 +152,30 @@ public abstract class AbstractAccountAuthenticator { public void confirmCredentials(IAccountAuthenticatorResponse response, Account account, Bundle options) throws RemoteException { + if (Log.isLoggable(TAG, Log.VERBOSE)) { + Log.v(TAG, "confirmCredentials: " + account); + } checkBinderPermission(); try { final Bundle result = AbstractAccountAuthenticator.this.confirmCredentials( new AccountAuthenticatorResponse(response), account, options); + if (Log.isLoggable(TAG, Log.VERBOSE)) { + result.keySet(); // force it to be unparcelled + Log.v(TAG, "confirmCredentials: result " + + AccountManager.sanitizeResult(result)); + } if (result != null) { response.onResult(result); } } catch (NetworkErrorException e) { + if (Log.isLoggable(TAG, Log.VERBOSE)) { + Log.v(TAG, "confirmCredentials", e); + } response.onError(AccountManager.ERROR_CODE_NETWORK_ERROR, e.getMessage()); } catch (UnsupportedOperationException e) { + if (Log.isLoggable(TAG, Log.VERBOSE)) { + Log.v(TAG, "confirmCredentials", e); + } response.onError(AccountManager.ERROR_CODE_UNSUPPORTED_OPERATION, "confirmCredentials not supported"); } @@ -149,16 +184,32 @@ public abstract class AbstractAccountAuthenticator { public void getAuthTokenLabel(IAccountAuthenticatorResponse response, String authTokenType) throws RemoteException { + if (Log.isLoggable(TAG, Log.VERBOSE)) { + Log.v(TAG, "getAuthTokenLabel: authTokenType " + authTokenType); + } checkBinderPermission(); try { Bundle result = new Bundle(); result.putString(AccountManager.KEY_AUTH_TOKEN_LABEL, AbstractAccountAuthenticator.this.getAuthTokenLabel(authTokenType)); - response.onResult(result); + if (Log.isLoggable(TAG, Log.VERBOSE)) { + result.keySet(); // force it to be unparcelled + Log.v(TAG, "getAuthTokenLabel: result " + + AccountManager.sanitizeResult(result)); + } + if (result != null) { + response.onResult(result); + } } catch (IllegalArgumentException e) { + if (Log.isLoggable(TAG, Log.VERBOSE)) { + Log.v(TAG, "getAuthTokenLabel", e); + } response.onError(AccountManager.ERROR_CODE_BAD_ARGUMENTS, "unknown authTokenType"); } catch (UnsupportedOperationException e) { + if (Log.isLoggable(TAG, Log.VERBOSE)) { + Log.v(TAG, "getAuthTokenLabel", e); + } response.onError(AccountManager.ERROR_CODE_UNSUPPORTED_OPERATION, "getAuthTokenTypeLabel not supported"); } @@ -167,35 +218,64 @@ public abstract class AbstractAccountAuthenticator { public void getAuthToken(IAccountAuthenticatorResponse response, Account account, String authTokenType, Bundle loginOptions) throws RemoteException { + if (Log.isLoggable(TAG, Log.VERBOSE)) { + Log.v(TAG, "getAuthToken: " + account + + ", authTokenType " + authTokenType); + } checkBinderPermission(); try { final Bundle result = AbstractAccountAuthenticator.this.getAuthToken( new AccountAuthenticatorResponse(response), account, authTokenType, loginOptions); + if (Log.isLoggable(TAG, Log.VERBOSE)) { + result.keySet(); // force it to be unparcelled + Log.v(TAG, "getAuthToken: result " + AccountManager.sanitizeResult(result)); + } if (result != null) { response.onResult(result); } } catch (UnsupportedOperationException e) { + if (Log.isLoggable(TAG, Log.VERBOSE)) { + Log.v(TAG, "getAuthToken", e); + } response.onError(AccountManager.ERROR_CODE_UNSUPPORTED_OPERATION, "getAuthToken not supported"); } catch (NetworkErrorException e) { + if (Log.isLoggable(TAG, Log.VERBOSE)) { + Log.v(TAG, "getAuthToken", e); + } response.onError(AccountManager.ERROR_CODE_NETWORK_ERROR, e.getMessage()); } } public void updateCredentials(IAccountAuthenticatorResponse response, Account account, String authTokenType, Bundle loginOptions) throws RemoteException { + if (Log.isLoggable(TAG, Log.VERBOSE)) { + Log.v(TAG, "updateCredentials: " + account + + ", authTokenType " + authTokenType); + } checkBinderPermission(); try { final Bundle result = AbstractAccountAuthenticator.this.updateCredentials( new AccountAuthenticatorResponse(response), account, authTokenType, loginOptions); + if (Log.isLoggable(TAG, Log.VERBOSE)) { + result.keySet(); // force it to be unparcelled + Log.v(TAG, "updateCredentials: result " + + AccountManager.sanitizeResult(result)); + } if (result != null) { response.onResult(result); } } catch (NetworkErrorException e) { + if (Log.isLoggable(TAG, Log.VERBOSE)) { + Log.v(TAG, "updateCredentials", e); + } response.onError(AccountManager.ERROR_CODE_NETWORK_ERROR, e.getMessage()); } catch (UnsupportedOperationException e) { + if (Log.isLoggable(TAG, Log.VERBOSE)) { + Log.v(TAG, "updateCredentials", e); + } response.onError(AccountManager.ERROR_CODE_UNSUPPORTED_OPERATION, "updateCredentials not supported"); } diff --git a/core/java/android/accounts/AccountAuthenticatorResponse.java b/core/java/android/accounts/AccountAuthenticatorResponse.java index 7c09fbf..614e139 100644 --- a/core/java/android/accounts/AccountAuthenticatorResponse.java +++ b/core/java/android/accounts/AccountAuthenticatorResponse.java @@ -20,11 +20,14 @@ import android.os.Bundle; import android.os.Parcelable; import android.os.Parcel; import android.os.RemoteException; +import android.util.Log; /** * Object used to communicate responses back to the AccountManager */ public class AccountAuthenticatorResponse implements Parcelable { + private static final String TAG = "AccountAuthenticator"; + private IAccountAuthenticatorResponse mAccountAuthenticatorResponse; /** @@ -40,6 +43,11 @@ public class AccountAuthenticatorResponse implements Parcelable { } public void onResult(Bundle result) { + if (Log.isLoggable(TAG, Log.VERBOSE)) { + result.keySet(); // force it to be unparcelled + Log.v(TAG, "AccountAuthenticatorResponse.onResult: " + + AccountManager.sanitizeResult(result)); + } try { mAccountAuthenticatorResponse.onResult(result); } catch (RemoteException e) { @@ -48,6 +56,9 @@ public class AccountAuthenticatorResponse implements Parcelable { } public void onRequestContinued() { + if (Log.isLoggable(TAG, Log.VERBOSE)) { + Log.v(TAG, "AccountAuthenticatorResponse.onRequestContinued"); + } try { mAccountAuthenticatorResponse.onRequestContinued(); } catch (RemoteException e) { @@ -56,6 +67,9 @@ public class AccountAuthenticatorResponse implements Parcelable { } public void onError(int errorCode, String errorMessage) { + if (Log.isLoggable(TAG, Log.VERBOSE)) { + Log.v(TAG, "AccountAuthenticatorResponse.onError: " + errorCode + ", " + errorMessage); + } try { mAccountAuthenticatorResponse.onError(errorCode, errorMessage); } catch (RemoteException e) { diff --git a/core/java/android/accounts/AccountManager.java b/core/java/android/accounts/AccountManager.java index 1bb1d0f..8356029 100644 --- a/core/java/android/accounts/AccountManager.java +++ b/core/java/android/accounts/AccountManager.java @@ -29,6 +29,7 @@ import android.os.RemoteException; import android.os.Parcelable; import android.os.Build; import android.util.Log; +import android.text.TextUtils; import java.io.IOException; import java.util.concurrent.Callable; @@ -223,6 +224,19 @@ public class AccountManager { } /** + * @hide for internal use only + */ + public static Bundle sanitizeResult(Bundle result) { + if (result.containsKey(KEY_AUTHTOKEN) + && !TextUtils.isEmpty(result.getString(KEY_AUTHTOKEN))) { + final Bundle newResult = new Bundle(result); + newResult.putString(KEY_AUTHTOKEN, "<omitted for logging purposes>"); + return newResult; + } + return result; + } + + /** * Gets an AccountManager instance associated with a Context. * The {@link Context} will be used as long as the AccountManager is * active, so make sure to use a {@link Context} whose lifetime is @@ -1447,6 +1461,7 @@ public class AccountManager { final Bundle mAddAccountOptions; final Bundle mLoginOptions; final AccountManagerCallback<Bundle> mMyCallback; + private volatile int mNumAccounts = 0; public void doWork() throws RemoteException { getAccountsByTypeAndFeatures(mAccountType, mFeatures, @@ -1466,6 +1481,8 @@ public class AccountManager { return; } + mNumAccounts = accounts.length; + if (accounts.length == 0) { if (mActivity != null) { // no accounts, add one now. pretend that the user directly @@ -1538,7 +1555,21 @@ public class AccountManager { public void run(AccountManagerFuture<Bundle> future) { try { - set(future.getResult()); + final Bundle result = future.getResult(); + if (mNumAccounts == 0) { + final String accountName = result.getString(KEY_ACCOUNT_NAME); + final String accountType = result.getString(KEY_ACCOUNT_TYPE); + if (TextUtils.isEmpty(accountName) || TextUtils.isEmpty(accountType)) { + setException(new AuthenticatorException("account not in result")); + return; + } + final Account account = new Account(accountName, accountType); + mNumAccounts = 1; + getAuthToken(account, mAuthTokenType, null /* options */, mActivity, + mMyCallback, mHandler); + return; + } + set(result); } catch (OperationCanceledException e) { cancel(true /* mayInterruptIfRUnning */); } catch (IOException e) { diff --git a/core/java/android/app/SearchDialog.java b/core/java/android/app/SearchDialog.java index 3e426f5..bf9b021 100644 --- a/core/java/android/app/SearchDialog.java +++ b/core/java/android/app/SearchDialog.java @@ -668,7 +668,10 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS // The user changed the query, remember it. mUserQuery = s == null ? "" : s.toString(); } - updateVoiceButton(mSearchAutoComplete.isEmpty()); + // Always want to show the microphone if the context is voice. + updateVoiceButton(mSearchAutoComplete.isEmpty() + || (mAppSearchData != null && mAppSearchData.getBoolean( + SearchManager.CONTEXT_IS_VOICE))); } public void afterTextChanged(Editable s) { diff --git a/core/java/android/app/SearchManager.java b/core/java/android/app/SearchManager.java index 0ed572a..625b120 100644 --- a/core/java/android/app/SearchManager.java +++ b/core/java/android/app/SearchManager.java @@ -1544,6 +1544,15 @@ public class SearchManager public final static String INTENT_ACTION_NONE = "android.search.action.ZILCH"; /** + * This means that context is voice, and therefore the SearchDialog should + * continue showing the microphone until the user indicates that he/she does + * not want to re-speak (e.g. by typing). + * + * @hide + */ + public final static String CONTEXT_IS_VOICE = "android.search.CONTEXT_IS_VOICE"; + + /** * Reference to the shared system search service. */ private static ISearchManager mService; diff --git a/core/java/android/content/ContentProviderNative.java b/core/java/android/content/ContentProviderNative.java index de52e65..fdb3d20 100644 --- a/core/java/android/content/ContentProviderNative.java +++ b/core/java/android/content/ContentProviderNative.java @@ -73,7 +73,10 @@ abstract public class ContentProviderNative extends Binder implements IContentPr case QUERY_TRANSACTION: { data.enforceInterface(IContentProvider.descriptor); + Uri url = Uri.CREATOR.createFromParcel(data); + + // String[] projection int num = data.readInt(); String[] projection = null; if (num > 0) { @@ -82,6 +85,8 @@ abstract public class ContentProviderNative extends Binder implements IContentPr projection[i] = data.readString(); } } + + // String selection, String[] selectionArgs... String selection = data.readString(); num = data.readInt(); String[] selectionArgs = null; @@ -91,19 +96,33 @@ abstract public class ContentProviderNative extends Binder implements IContentPr selectionArgs[i] = data.readString(); } } + String sortOrder = data.readString(); IContentObserver observer = IContentObserver.Stub. asInterface(data.readStrongBinder()); CursorWindow window = CursorWindow.CREATOR.createFromParcel(data); + // Flag for whether caller wants the number of + // rows in the cursor and the position of the + // "_id" column index (or -1 if non-existent) + // Only to be returned if binder != null. + boolean wantsCursorMetadata = data.readInt() != 0; + IBulkCursor bulkCursor = bulkQuery(url, projection, selection, selectionArgs, sortOrder, observer, window); reply.writeNoException(); if (bulkCursor != null) { reply.writeStrongBinder(bulkCursor.asBinder()); + + if (wantsCursorMetadata) { + reply.writeInt(bulkCursor.count()); + reply.writeInt(BulkCursorToCursorAdaptor.findRowIdColumnIndex( + bulkCursor.getColumnNames())); + } } else { reply.writeStrongBinder(null); } + return true; } @@ -266,9 +285,12 @@ final class ContentProviderProxy implements IContentProvider return mRemote; } - public IBulkCursor bulkQuery(Uri url, String[] projection, - String selection, String[] selectionArgs, String sortOrder, IContentObserver observer, - CursorWindow window) throws RemoteException { + // Like bulkQuery() but sets up provided 'adaptor' if not null. + private IBulkCursor bulkQueryInternal( + Uri url, String[] projection, + String selection, String[] selectionArgs, String sortOrder, + IContentObserver observer, CursorWindow window, + BulkCursorToCursorAdaptor adaptor) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); @@ -297,6 +319,12 @@ final class ContentProviderProxy implements IContentProvider data.writeStrongBinder(observer.asBinder()); window.writeToParcel(data, 0); + // Flag for whether or not we want the number of rows in the + // cursor and the position of the "_id" column index (or -1 if + // non-existent). Only to be returned if binder != null. + final boolean wantsCursorMetadata = (adaptor != null); + data.writeInt(wantsCursorMetadata ? 1 : 0); + mRemote.transact(IContentProvider.QUERY_TRANSACTION, data, reply, 0); DatabaseUtils.readExceptionFromParcel(reply); @@ -305,26 +333,43 @@ final class ContentProviderProxy implements IContentProvider IBinder bulkCursorBinder = reply.readStrongBinder(); if (bulkCursorBinder != null) { bulkCursor = BulkCursorNative.asInterface(bulkCursorBinder); + + if (wantsCursorMetadata) { + int rowCount = reply.readInt(); + int idColumnPosition = reply.readInt(); + if (bulkCursor != null) { + adaptor.set(bulkCursor, rowCount, idColumnPosition); + } + } } - + data.recycle(); reply.recycle(); - + return bulkCursor; } + public IBulkCursor bulkQuery(Uri url, String[] projection, + String selection, String[] selectionArgs, String sortOrder, IContentObserver observer, + CursorWindow window) throws RemoteException { + return bulkQueryInternal( + url, projection, selection, selectionArgs, sortOrder, + observer, window, + null /* BulkCursorToCursorAdaptor */); + } + public Cursor query(Uri url, String[] projection, String selection, String[] selectionArgs, String sortOrder) throws RemoteException { //TODO make a pool of windows so we can reuse memory dealers CursorWindow window = new CursorWindow(false /* window will be used remotely */); BulkCursorToCursorAdaptor adaptor = new BulkCursorToCursorAdaptor(); - IBulkCursor bulkCursor = bulkQuery(url, projection, selection, selectionArgs, sortOrder, - adaptor.getObserver(), window); - + IBulkCursor bulkCursor = bulkQueryInternal( + url, projection, selection, selectionArgs, sortOrder, + adaptor.getObserver(), window, + adaptor); if (bulkCursor == null) { return null; } - adaptor.set(bulkCursor); return adaptor; } diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java index 8773f59..423f4bc 100644 --- a/core/java/android/content/pm/ApplicationInfo.java +++ b/core/java/android/content/pm/ApplicationInfo.java @@ -237,7 +237,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { * * {@hide} */ - public static final int FLAG_ON_SDCARD = 1<<20; + public static final int FLAG_EXTERNAL_STORAGE = 1<<20; /** * Value for {@link #flags}: Set to true if the application is diff --git a/core/java/android/content/pm/PackageInfo.java b/core/java/android/content/pm/PackageInfo.java index c003355..0964425 100644 --- a/core/java/android/content/pm/PackageInfo.java +++ b/core/java/android/content/pm/PackageInfo.java @@ -151,7 +151,7 @@ public class PackageInfo implements Parcelable { */ public static final int INSTALL_LOCATION_PREFER_EXTERNAL = 2; /** - * The launch mode style requested by the activity. From the + * The install location requested by the activity. From the * {@link android.R.attr#installLocation} attribute, one of * {@link #INSTALL_LOCATION_AUTO}, * {@link #INSTALL_LOCATION_INTERNAL_ONLY}, diff --git a/core/java/android/content/pm/PackageInfoLite.aidl b/core/java/android/content/pm/PackageInfoLite.aidl new file mode 100755 index 0000000..2c80942 --- /dev/null +++ b/core/java/android/content/pm/PackageInfoLite.aidl @@ -0,0 +1,20 @@ +/* //device/java/android/android/view/WindowManager.aidl +** +** Copyright 2007, 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.content.pm; + +parcelable PackageInfoLite; diff --git a/core/java/android/content/pm/PackageInfoLite.java b/core/java/android/content/pm/PackageInfoLite.java new file mode 100644 index 0000000..2f38ece --- /dev/null +++ b/core/java/android/content/pm/PackageInfoLite.java @@ -0,0 +1,63 @@ +package android.content.pm; + +import android.os.Parcel; +import android.os.Parcelable; + +/** + * Basic information about a package as specified in its manifest. + * Utility class used in PackageManager methods + * @hide + */ +public class PackageInfoLite implements Parcelable { + /** + * The name of this package. From the <manifest> tag's "name" + * attribute. + */ + public String packageName; + + /** + * Specifies the recommended install location. Can be one of + * {@link #PackageHelper.RECOMMEND_INSTALL_INTERNAL} to install on internal storage + * {@link #PackageHelper.RECOMMEND_INSTALL_EXTERNAL} to install on external media + * {@link PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE} for storage errors + * {@link PackageHelper.RECOMMEND_FAILED_INVALID_APK} for parse errors. + */ + public int recommendedInstallLocation; + public int installLocation; + + public PackageInfoLite() { + } + + public String toString() { + return "PackageInfoLite{" + + Integer.toHexString(System.identityHashCode(this)) + + " " + packageName + "}"; + } + + public int describeContents() { + return 0; + } + + public void writeToParcel(Parcel dest, int parcelableFlags) { + dest.writeString(packageName); + dest.writeInt(recommendedInstallLocation); + dest.writeInt(installLocation); + } + + public static final Parcelable.Creator<PackageInfoLite> CREATOR + = new Parcelable.Creator<PackageInfoLite>() { + public PackageInfoLite createFromParcel(Parcel source) { + return new PackageInfoLite(source); + } + + public PackageInfoLite[] newArray(int size) { + return new PackageInfoLite[size]; + } + }; + + private PackageInfoLite(Parcel source) { + packageName = source.readString(); + recommendedInstallLocation = source.readInt(); + installLocation = source.readInt(); + } +}
\ No newline at end of file diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index 7a0337cd..9736b01 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -136,7 +136,20 @@ public class PackageParser { enabledRes = _enabledRes; } } - + + /* Light weight package info. + * @hide + */ + public static class PackageLite { + public String packageName; + public int installLocation; + public String mScanPath; + public PackageLite(String packageName, int installLocation) { + this.packageName = packageName; + this.installLocation = installLocation; + } + } + private ParsePackageItemArgs mParseInstrumentationArgs; private ParseComponentArgs mParseActivityArgs; private ParseComponentArgs mParseActivityAliasArgs; @@ -562,7 +575,14 @@ public class PackageParser { return true; } - public static String parsePackageName(String packageFilePath, int flags) { + /* + * Utility method that retrieves just the package name and install + * location from the apk location at the given file path. + * @param packageFilePath file location of the apk + * @param flags Special parse flags + * @return PackageLite object with package information. + */ + public static PackageLite parsePackageLite(String packageFilePath, int flags) { XmlResourceParser parser = null; AssetManager assmgr = null; try { @@ -577,9 +597,9 @@ public class PackageParser { } AttributeSet attrs = parser; String errors[] = new String[1]; - String packageName = null; + PackageLite packageLite = null; try { - packageName = parsePackageName(parser, attrs, flags, errors); + packageLite = parsePackageLite(parser, attrs, flags, errors); } catch (IOException e) { Log.w(TAG, packageFilePath, e); } catch (XmlPullParserException e) { @@ -588,11 +608,11 @@ public class PackageParser { if (parser != null) parser.close(); if (assmgr != null) assmgr.close(); } - if (packageName == null) { - Log.e(TAG, "parsePackageName error: " + errors[0]); + if (packageLite == null) { + Log.e(TAG, "parsePackageLite error: " + errors[0]); return null; } - return packageName; + return packageLite; } private static String validateName(String name, boolean requiresSeparator) { @@ -656,6 +676,49 @@ public class PackageParser { return pkgName.intern(); } + private static PackageLite parsePackageLite(XmlPullParser parser, + AttributeSet attrs, int flags, String[] outError) + throws IOException, XmlPullParserException { + + int type; + while ((type=parser.next()) != parser.START_TAG + && type != parser.END_DOCUMENT) { + ; + } + + if (type != parser.START_TAG) { + outError[0] = "No start tag found"; + return null; + } + if ((flags&PARSE_CHATTY) != 0 && Config.LOGV) Log.v( + TAG, "Root element name: '" + parser.getName() + "'"); + if (!parser.getName().equals("manifest")) { + outError[0] = "No <manifest> tag"; + return null; + } + String pkgName = attrs.getAttributeValue(null, "package"); + if (pkgName == null || pkgName.length() == 0) { + outError[0] = "<manifest> does not specify package"; + return null; + } + String nameError = validateName(pkgName, true); + if (nameError != null && !"android".equals(pkgName)) { + outError[0] = "<manifest> specifies bad package name \"" + + pkgName + "\": " + nameError; + return null; + } + int installLocation = PackageInfo.INSTALL_LOCATION_AUTO; + for (int i = 0; i < attrs.getAttributeCount(); i++) { + String attr = attrs.getAttributeName(i); + if (attr.equals("installLocation")) { + installLocation = attrs.getAttributeIntValue(i, + PackageInfo.INSTALL_LOCATION_AUTO); + break; + } + } + return new PackageLite(pkgName.intern(), installLocation); + } + /** * Temporary. */ @@ -692,14 +755,14 @@ public class PackageParser { com.android.internal.R.styleable.AndroidManifest); pkg.mVersionCode = sa.getInteger( com.android.internal.R.styleable.AndroidManifest_versionCode, 0); - pkg.mVersionName = sa.getNonResourceString( - com.android.internal.R.styleable.AndroidManifest_versionName); + pkg.mVersionName = sa.getNonConfigurationString( + com.android.internal.R.styleable.AndroidManifest_versionName, 0); if (pkg.mVersionName != null) { pkg.mVersionName = pkg.mVersionName.intern(); } - String str = sa.getNonResourceString( - com.android.internal.R.styleable.AndroidManifest_sharedUserId); - if (str != null) { + String str = sa.getNonConfigurationString( + com.android.internal.R.styleable.AndroidManifest_sharedUserId, 0); + if (str != null && str.length() > 0) { String nameError = validateName(str, true); if (nameError != null && !"android".equals(pkgName)) { outError[0] = "<manifest> specifies bad sharedUserId name \"" @@ -765,6 +828,8 @@ public class PackageParser { sa = res.obtainAttributes(attrs, com.android.internal.R.styleable.AndroidManifestUsesPermission); + // Note: don't allow this value to be a reference to a resource + // that may change. String name = sa.getNonResourceString( com.android.internal.R.styleable.AndroidManifestUsesPermission_name); @@ -808,6 +873,8 @@ public class PackageParser { FeatureInfo fi = new FeatureInfo(); sa = res.obtainAttributes(attrs, com.android.internal.R.styleable.AndroidManifestUsesFeature); + // Note: don't allow this value to be a reference to a resource + // that may change. fi.name = sa.getNonResourceString( com.android.internal.R.styleable.AndroidManifestUsesFeature_name); if (fi.name == null) { @@ -939,6 +1006,8 @@ public class PackageParser { sa = res.obtainAttributes(attrs, com.android.internal.R.styleable.AndroidManifestProtectedBroadcast); + // Note: don't allow this value to be a reference to a resource + // that may change. String name = sa.getNonResourceString( com.android.internal.R.styleable.AndroidManifestProtectedBroadcast_name); @@ -964,8 +1033,8 @@ public class PackageParser { sa = res.obtainAttributes(attrs, com.android.internal.R.styleable.AndroidManifestOriginalPackage); - String orig =sa.getNonResourceString( - com.android.internal.R.styleable.AndroidManifestOriginalPackage_name); + String orig =sa.getNonConfigurationString( + com.android.internal.R.styleable.AndroidManifestOriginalPackage_name, 0); if (!pkg.packageName.equals(orig)) { if (pkg.mOriginalPackages == null) { pkg.mOriginalPackages = new ArrayList<String>(); @@ -982,8 +1051,8 @@ public class PackageParser { sa = res.obtainAttributes(attrs, com.android.internal.R.styleable.AndroidManifestOriginalPackage); - String name = sa.getNonResourceString( - com.android.internal.R.styleable.AndroidManifestOriginalPackage_name); + String name = sa.getNonConfigurationString( + com.android.internal.R.styleable.AndroidManifestOriginalPackage_name, 0); sa.recycle(); @@ -1208,6 +1277,8 @@ public class PackageParser { return null; } + // Note: don't allow this value to be a reference to a resource + // that may change. perm.info.group = sa.getNonResourceString( com.android.internal.R.styleable.AndroidManifestPermission_permissionGroup); if (perm.info.group != null) { @@ -1312,6 +1383,8 @@ public class PackageParser { } String str; + // Note: don't allow this value to be a reference to a resource + // that may change. str = sa.getNonResourceString( com.android.internal.R.styleable.AndroidManifestInstrumentation_targetPackage); a.info.targetPackage = str != null ? str.intern() : null; @@ -1352,8 +1425,8 @@ public class PackageParser { TypedArray sa = res.obtainAttributes(attrs, com.android.internal.R.styleable.AndroidManifestApplication); - String name = sa.getNonResourceString( - com.android.internal.R.styleable.AndroidManifestApplication_name); + String name = sa.getNonConfigurationString( + com.android.internal.R.styleable.AndroidManifestApplication_name, 0); if (name != null) { ai.className = buildClassName(pkgName, name, outError); if (ai.className == null) { @@ -1363,8 +1436,8 @@ public class PackageParser { } } - String manageSpaceActivity = sa.getNonResourceString( - com.android.internal.R.styleable.AndroidManifestApplication_manageSpaceActivity); + String manageSpaceActivity = sa.getNonConfigurationString( + com.android.internal.R.styleable.AndroidManifestApplication_manageSpaceActivity, 0); if (manageSpaceActivity != null) { ai.manageSpaceActivityName = buildClassName(pkgName, manageSpaceActivity, outError); @@ -1377,8 +1450,8 @@ public class PackageParser { // backupAgent, killAfterRestore, restoreNeedsApplication, and restoreAnyVersion // are only relevant if backup is possible for the given application. - String backupAgent = sa.getNonResourceString( - com.android.internal.R.styleable.AndroidManifestApplication_backupAgent); + String backupAgent = sa.getNonConfigurationString( + com.android.internal.R.styleable.AndroidManifestApplication_backupAgent, 0); if (backupAgent != null) { ai.backupAgentName = buildClassName(pkgName, backupAgent, outError); if (false) { @@ -1430,7 +1503,7 @@ public class PackageParser { } if ((flags & PARSE_ON_SDCARD) != 0) { - ai.flags |= ApplicationInfo.FLAG_ON_SDCARD; + ai.flags |= ApplicationInfo.FLAG_EXTERNAL_STORAGE; } if (sa.getBoolean( @@ -1476,21 +1549,22 @@ public class PackageParser { } String str; - str = sa.getNonResourceString( - com.android.internal.R.styleable.AndroidManifestApplication_permission); + str = sa.getNonConfigurationString( + com.android.internal.R.styleable.AndroidManifestApplication_permission, 0); ai.permission = (str != null && str.length() > 0) ? str.intern() : null; - str = sa.getNonResourceString( - com.android.internal.R.styleable.AndroidManifestApplication_taskAffinity); + str = sa.getNonConfigurationString( + com.android.internal.R.styleable.AndroidManifestApplication_taskAffinity, 0); ai.taskAffinity = buildTaskAffinityName(ai.packageName, ai.packageName, str, outError); if (outError[0] == null) { - ai.processName = buildProcessName(ai.packageName, null, sa.getNonResourceString( - com.android.internal.R.styleable.AndroidManifestApplication_process), + ai.processName = buildProcessName(ai.packageName, null, sa.getNonConfigurationString( + com.android.internal.R.styleable.AndroidManifestApplication_process, 0), flags, mSeparateProcesses, outError); - ai.enabled = sa.getBoolean(com.android.internal.R.styleable.AndroidManifestApplication_enabled, true); + ai.enabled = sa.getBoolean( + com.android.internal.R.styleable.AndroidManifestApplication_enabled, true); } sa.recycle(); @@ -1569,6 +1643,8 @@ public class PackageParser { sa = res.obtainAttributes(attrs, com.android.internal.R.styleable.AndroidManifestUsesLibrary); + // Note: don't allow this value to be a reference to a resource + // that may change. String lname = sa.getNonResourceString( com.android.internal.R.styleable.AndroidManifestUsesLibrary_name); boolean req = sa.getBoolean( @@ -1618,7 +1694,7 @@ public class PackageParser { private boolean parsePackageItemInfo(Package owner, PackageItemInfo outInfo, String[] outError, String tag, TypedArray sa, int nameRes, int labelRes, int iconRes) { - String name = sa.getNonResourceString(nameRes); + String name = sa.getNonConfigurationString(nameRes, 0); if (name == null) { outError[0] = tag + " does not specify android:name"; return false; @@ -1684,16 +1760,16 @@ public class PackageParser { com.android.internal.R.styleable.AndroidManifestActivity_theme, 0); String str; - str = sa.getNonResourceString( - com.android.internal.R.styleable.AndroidManifestActivity_permission); + str = sa.getNonConfigurationString( + com.android.internal.R.styleable.AndroidManifestActivity_permission, 0); if (str == null) { a.info.permission = owner.applicationInfo.permission; } else { a.info.permission = str.length() > 0 ? str.toString().intern() : null; } - str = sa.getNonResourceString( - com.android.internal.R.styleable.AndroidManifestActivity_taskAffinity); + str = sa.getNonConfigurationString( + com.android.internal.R.styleable.AndroidManifestActivity_taskAffinity, 0); a.info.taskAffinity = buildTaskAffinityName(owner.applicationInfo.packageName, owner.applicationInfo.taskAffinity, str, outError); @@ -1839,8 +1915,8 @@ public class PackageParser { TypedArray sa = res.obtainAttributes(attrs, com.android.internal.R.styleable.AndroidManifestActivityAlias); - String targetActivity = sa.getNonResourceString( - com.android.internal.R.styleable.AndroidManifestActivityAlias_targetActivity); + String targetActivity = sa.getNonConfigurationString( + com.android.internal.R.styleable.AndroidManifestActivityAlias_targetActivity, 0); if (targetActivity == null) { outError[0] = "<activity-alias> does not specify android:targetActivity"; sa.recycle(); @@ -1917,8 +1993,8 @@ public class PackageParser { } String str; - str = sa.getNonResourceString( - com.android.internal.R.styleable.AndroidManifestActivityAlias_permission); + str = sa.getNonConfigurationString( + com.android.internal.R.styleable.AndroidManifestActivityAlias_permission, 0); if (str != null) { a.info.permission = str.length() > 0 ? str.toString().intern() : null; } @@ -2005,17 +2081,17 @@ public class PackageParser { p.info.exported = sa.getBoolean( com.android.internal.R.styleable.AndroidManifestProvider_exported, true); - String cpname = sa.getNonResourceString( - com.android.internal.R.styleable.AndroidManifestProvider_authorities); + String cpname = sa.getNonConfigurationString( + com.android.internal.R.styleable.AndroidManifestProvider_authorities, 0); p.info.isSyncable = sa.getBoolean( com.android.internal.R.styleable.AndroidManifestProvider_syncable, false); - String permission = sa.getNonResourceString( - com.android.internal.R.styleable.AndroidManifestProvider_permission); - String str = sa.getNonResourceString( - com.android.internal.R.styleable.AndroidManifestProvider_readPermission); + String permission = sa.getNonConfigurationString( + com.android.internal.R.styleable.AndroidManifestProvider_permission, 0); + String str = sa.getNonConfigurationString( + com.android.internal.R.styleable.AndroidManifestProvider_readPermission, 0); if (str == null) { str = permission; } @@ -2025,8 +2101,8 @@ public class PackageParser { p.info.readPermission = str.length() > 0 ? str.toString().intern() : null; } - str = sa.getNonResourceString( - com.android.internal.R.styleable.AndroidManifestProvider_writePermission); + str = sa.getNonConfigurationString( + com.android.internal.R.styleable.AndroidManifestProvider_writePermission, 0); if (str == null) { str = permission; } @@ -2089,20 +2165,20 @@ public class PackageParser { PatternMatcher pa = null; - String str = sa.getNonResourceString( - com.android.internal.R.styleable.AndroidManifestGrantUriPermission_path); + String str = sa.getNonConfigurationString( + com.android.internal.R.styleable.AndroidManifestGrantUriPermission_path, 0); if (str != null) { pa = new PatternMatcher(str, PatternMatcher.PATTERN_LITERAL); } - str = sa.getNonResourceString( - com.android.internal.R.styleable.AndroidManifestGrantUriPermission_pathPrefix); + str = sa.getNonConfigurationString( + com.android.internal.R.styleable.AndroidManifestGrantUriPermission_pathPrefix, 0); if (str != null) { pa = new PatternMatcher(str, PatternMatcher.PATTERN_PREFIX); } - str = sa.getNonResourceString( - com.android.internal.R.styleable.AndroidManifestGrantUriPermission_pathPattern); + str = sa.getNonConfigurationString( + com.android.internal.R.styleable.AndroidManifestGrantUriPermission_pathPattern, 0); if (str != null) { pa = new PatternMatcher(str, PatternMatcher.PATTERN_SIMPLE_GLOB); } @@ -2140,15 +2216,15 @@ public class PackageParser { PathPermission pa = null; - String permission = sa.getNonResourceString( - com.android.internal.R.styleable.AndroidManifestPathPermission_permission); - String readPermission = sa.getNonResourceString( - com.android.internal.R.styleable.AndroidManifestPathPermission_readPermission); + String permission = sa.getNonConfigurationString( + com.android.internal.R.styleable.AndroidManifestPathPermission_permission, 0); + String readPermission = sa.getNonConfigurationString( + com.android.internal.R.styleable.AndroidManifestPathPermission_readPermission, 0); if (readPermission == null) { readPermission = permission; } - String writePermission = sa.getNonResourceString( - com.android.internal.R.styleable.AndroidManifestPathPermission_writePermission); + String writePermission = sa.getNonConfigurationString( + com.android.internal.R.styleable.AndroidManifestPathPermission_writePermission, 0); if (writePermission == null) { writePermission = permission; } @@ -2175,22 +2251,22 @@ public class PackageParser { return false; } - String path = sa.getNonResourceString( - com.android.internal.R.styleable.AndroidManifestPathPermission_path); + String path = sa.getNonConfigurationString( + com.android.internal.R.styleable.AndroidManifestPathPermission_path, 0); if (path != null) { pa = new PathPermission(path, PatternMatcher.PATTERN_LITERAL, readPermission, writePermission); } - path = sa.getNonResourceString( - com.android.internal.R.styleable.AndroidManifestPathPermission_pathPrefix); + path = sa.getNonConfigurationString( + com.android.internal.R.styleable.AndroidManifestPathPermission_pathPrefix, 0); if (path != null) { pa = new PathPermission(path, PatternMatcher.PATTERN_PREFIX, readPermission, writePermission); } - path = sa.getNonResourceString( - com.android.internal.R.styleable.AndroidManifestPathPermission_pathPattern); + path = sa.getNonConfigurationString( + com.android.internal.R.styleable.AndroidManifestPathPermission_pathPattern, 0); if (path != null) { pa = new PathPermission(path, PatternMatcher.PATTERN_SIMPLE_GLOB, readPermission, writePermission); @@ -2272,8 +2348,8 @@ public class PackageParser { com.android.internal.R.styleable.AndroidManifestService_exported, false); } - String str = sa.getNonResourceString( - com.android.internal.R.styleable.AndroidManifestService_permission); + String str = sa.getNonConfigurationString( + com.android.internal.R.styleable.AndroidManifestService_permission, 0); if (str == null) { s.info.permission = owner.applicationInfo.permission; } else { @@ -2370,8 +2446,8 @@ public class PackageParser { data = new Bundle(); } - String name = sa.getNonResourceString( - com.android.internal.R.styleable.AndroidManifestMetaData_name); + String name = sa.getNonConfigurationString( + com.android.internal.R.styleable.AndroidManifestMetaData_name, 0); if (name == null) { outError[0] = "<meta-data> requires an android:name attribute"; sa.recycle(); @@ -2489,8 +2565,8 @@ public class PackageParser { sa = res.obtainAttributes(attrs, com.android.internal.R.styleable.AndroidManifestData); - String str = sa.getNonResourceString( - com.android.internal.R.styleable.AndroidManifestData_mimeType); + String str = sa.getNonConfigurationString( + com.android.internal.R.styleable.AndroidManifestData_mimeType, 0); if (str != null) { try { outInfo.addDataType(str); @@ -2501,34 +2577,34 @@ public class PackageParser { } } - str = sa.getNonResourceString( - com.android.internal.R.styleable.AndroidManifestData_scheme); + str = sa.getNonConfigurationString( + com.android.internal.R.styleable.AndroidManifestData_scheme, 0); if (str != null) { outInfo.addDataScheme(str); } - String host = sa.getNonResourceString( - com.android.internal.R.styleable.AndroidManifestData_host); - String port = sa.getNonResourceString( - com.android.internal.R.styleable.AndroidManifestData_port); + String host = sa.getNonConfigurationString( + com.android.internal.R.styleable.AndroidManifestData_host, 0); + String port = sa.getNonConfigurationString( + com.android.internal.R.styleable.AndroidManifestData_port, 0); if (host != null) { outInfo.addDataAuthority(host, port); } - str = sa.getNonResourceString( - com.android.internal.R.styleable.AndroidManifestData_path); + str = sa.getNonConfigurationString( + com.android.internal.R.styleable.AndroidManifestData_path, 0); if (str != null) { outInfo.addDataPath(str, PatternMatcher.PATTERN_LITERAL); } - str = sa.getNonResourceString( - com.android.internal.R.styleable.AndroidManifestData_pathPrefix); + str = sa.getNonConfigurationString( + com.android.internal.R.styleable.AndroidManifestData_pathPrefix, 0); if (str != null) { outInfo.addDataPath(str, PatternMatcher.PATTERN_PREFIX); } - str = sa.getNonResourceString( - com.android.internal.R.styleable.AndroidManifestData_pathPattern); + str = sa.getNonConfigurationString( + com.android.internal.R.styleable.AndroidManifestData_pathPattern, 0); if (str != null) { outInfo.addDataPath(str, PatternMatcher.PATTERN_SIMPLE_GLOB); } @@ -2691,7 +2767,7 @@ public class PackageParser { public Component(final ParsePackageItemArgs args, final PackageItemInfo outInfo) { owner = args.owner; intents = new ArrayList<II>(0); - String name = args.sa.getNonResourceString(args.nameRes); + String name = args.sa.getNonConfigurationString(args.nameRes, 0); if (name == null) { className = null; args.outError[0] = args.tag + " does not specify android:name"; @@ -2730,7 +2806,8 @@ public class PackageParser { if (args.processRes != 0) { outInfo.processName = buildProcessName(owner.applicationInfo.packageName, - owner.applicationInfo.processName, args.sa.getNonResourceString(args.processRes), + owner.applicationInfo.processName, + args.sa.getNonConfigurationString(args.processRes, 0), args.flags, args.sepProcesses, args.outError); } diff --git a/core/java/android/content/res/TypedArray.java b/core/java/android/content/res/TypedArray.java index a7fb31d..09fdf8d 100644 --- a/core/java/android/content/res/TypedArray.java +++ b/core/java/android/content/res/TypedArray.java @@ -148,6 +148,42 @@ public class TypedArray { } /** + * @hide + * Retrieve the string value for the attribute at <var>index</var> that is + * not allowed to change with the given configurations. + * + * @param index Index of attribute to retrieve. + * @param allowedChangingConfigs Bit mask of configurations from + * ActivityInfo that are allowed to change. + * + * @return String holding string data. Any styling information is + * removed. Returns null if the attribute is not defined. + */ + public String getNonConfigurationString(int index, int allowedChangingConfigs) { + index *= AssetManager.STYLE_NUM_ENTRIES; + final int[] data = mData; + final int type = data[index+AssetManager.STYLE_TYPE]; + if ((data[index+AssetManager.STYLE_CHANGING_CONFIGURATIONS]&~allowedChangingConfigs) != 0) { + return null; + } + if (type == TypedValue.TYPE_NULL) { + return null; + } else if (type == TypedValue.TYPE_STRING) { + return loadStringValueAt(index).toString(); + } + + TypedValue v = mValue; + if (getValueAt(index, v)) { + Log.w(Resources.TAG, "Converting to string: " + v); + CharSequence cs = v.coerceToString(); + return cs != null ? cs.toString() : null; + } + Log.w(Resources.TAG, "getString of bad type: 0x" + + Integer.toHexString(type)); + return null; + } + + /** * Retrieve the boolean value for the attribute at <var>index</var>. * * @param index Index of attribute to retrieve. diff --git a/core/java/android/database/BulkCursorToCursorAdaptor.java b/core/java/android/database/BulkCursorToCursorAdaptor.java index cf30dd9..1469ea2 100644 --- a/core/java/android/database/BulkCursorToCursorAdaptor.java +++ b/core/java/android/database/BulkCursorToCursorAdaptor.java @@ -43,25 +43,43 @@ public final class BulkCursorToCursorAdaptor extends AbstractWindowedCursor { try { mCount = mBulkCursor.count(); mWantsAllOnMoveCalls = mBulkCursor.getWantsAllOnMoveCalls(); - + // Search for the rowID column index and set it for our parent mColumns = mBulkCursor.getColumnNames(); - int length = mColumns.length; - for (int i = 0; i < length; i++) { - if (mColumns[i].equals("_id")) { - mRowIdColumnIndex = i; - break; - } - } + mRowIdColumnIndex = findRowIdColumnIndex(mColumns); } catch (RemoteException ex) { Log.e(TAG, "Setup failed because the remote process is dead"); } } /** + * Version of set() that does fewer Binder calls if the caller + * already knows BulkCursorToCursorAdaptor's properties. + */ + public void set(IBulkCursor bulkCursor, int count, int idIndex) { + mBulkCursor = bulkCursor; + mColumns = null; // lazily retrieved + mCount = count; + mRowIdColumnIndex = idIndex; + } + + /** + * Returns column index of "_id" column, or -1 if not found. + */ + public static int findRowIdColumnIndex(String[] columnNames) { + int length = columnNames.length; + for (int i = 0; i < length; i++) { + if (columnNames[i].equals("_id")) { + return i; + } + } + return -1; + } + + /** * Gets a SelfDataChangeOberserver that can be sent to a remote * process to receive change notifications over IPC. - * + * * @return A SelfContentObserver hooked up to this Cursor */ public synchronized IContentObserver getObserver() { @@ -190,6 +208,14 @@ public final class BulkCursorToCursorAdaptor extends AbstractWindowedCursor { @Override public String[] getColumnNames() { + if (mColumns == null) { + try { + mColumns = mBulkCursor.getColumnNames(); + } catch (RemoteException ex) { + Log.e(TAG, "Unable to fetch column names because the remote process is dead"); + return null; + } + } return mColumns; } @@ -255,4 +281,3 @@ public final class BulkCursorToCursorAdaptor extends AbstractWindowedCursor { } } } - diff --git a/core/java/android/database/CursorWindow.java b/core/java/android/database/CursorWindow.java index 99db81b..c756825 100644 --- a/core/java/android/database/CursorWindow.java +++ b/core/java/android/database/CursorWindow.java @@ -44,7 +44,7 @@ public class CursorWindow extends SQLiteClosable implements Parcelable { /** * Returns the starting position of this window within the entire * Cursor's result set. - * + * * @return the starting position of this window within the entire * Cursor's result set. */ diff --git a/core/java/android/database/IBulkCursor.java b/core/java/android/database/IBulkCursor.java index 24354fd..46790a3 100644 --- a/core/java/android/database/IBulkCursor.java +++ b/core/java/android/database/IBulkCursor.java @@ -27,11 +27,10 @@ import java.util.Map; * This interface provides a low-level way to pass bulk cursor data across * both process and language boundries. Application code should use the Cursor * interface directly. - * + * * {@hide} */ -public interface IBulkCursor extends IInterface -{ +public interface IBulkCursor extends IInterface { /** * Returns a BulkCursorWindow, which either has a reference to a shared * memory segment with the rows, or an array of JSON strings. @@ -60,7 +59,7 @@ public interface IBulkCursor extends IInterface public boolean deleteRow(int position) throws RemoteException; public void deactivate() throws RemoteException; - + public void close() throws RemoteException; public int requery(IContentObserver observer, CursorWindow window) throws RemoteException; @@ -87,4 +86,3 @@ public interface IBulkCursor extends IInterface static final int RESPOND_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 10; static final int CLOSE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 11; } - diff --git a/core/java/android/database/sqlite/SQLiteClosable.java b/core/java/android/database/sqlite/SQLiteClosable.java index e589f34..71fa2c2 100644 --- a/core/java/android/database/sqlite/SQLiteClosable.java +++ b/core/java/android/database/sqlite/SQLiteClosable.java @@ -19,14 +19,15 @@ package android.database.sqlite; import android.database.CursorWindow; /** - * An object create from a SQLiteDatabase that can be closed. + * An object created from a SQLiteDatabase that can be closed. */ -public abstract class SQLiteClosable { +public abstract class SQLiteClosable { private int mReferenceCount = 1; private Object mLock = new Object(); + protected abstract void onAllReferencesReleased(); - protected void onAllReferencesReleasedFromContainer(){} - + protected void onAllReferencesReleasedFromContainer() {} + public void acquireReference() { synchronized(mLock) { if (mReferenceCount <= 0) { @@ -36,7 +37,7 @@ public abstract class SQLiteClosable { mReferenceCount++; } } - + public void releaseReference() { synchronized(mLock) { mReferenceCount--; @@ -45,14 +46,14 @@ public abstract class SQLiteClosable { } } } - + public void releaseReferenceFromContainer() { synchronized(mLock) { mReferenceCount--; if (mReferenceCount == 0) { onAllReferencesReleasedFromContainer(); } - } + } } private String getObjInfo() { diff --git a/core/java/android/database/sqlite/SQLiteCompiledSql.java b/core/java/android/database/sqlite/SQLiteCompiledSql.java index 4ccf6b0..18cc1d5 100644 --- a/core/java/android/database/sqlite/SQLiteCompiledSql.java +++ b/core/java/android/database/sqlite/SQLiteCompiledSql.java @@ -23,8 +23,8 @@ import android.util.Log; * Once a sql statement is compiled, it is cached in {@link SQLiteDatabase} * and it is released in one of the 2 following ways * 1. when {@link SQLiteDatabase} object is closed. - * 2. dalvikVM wants to reclaim some memory and releases it from the cache in - * {@link SQLiteDatabase}. + * 2. if this is not cached in {@link SQLiteDatabase}, {@link android.database.Cursor#close()} + * releaases this obj. */ /* package */ class SQLiteCompiledSql { @@ -127,7 +127,7 @@ import android.util.Log; try { if (nStatement == 0) return; // finalizer should NEVER get called - Log.w(TAG, "finalizer should never be called. sql: " + mSqlStmt, mStackTrace); + Log.w(TAG, "finalizer should never be called on sql: " + mSqlStmt, mStackTrace); releaseSqlStatement(); } finally { super.finalize(); diff --git a/core/java/android/database/sqlite/SQLiteProgram.java b/core/java/android/database/sqlite/SQLiteProgram.java index 6636473..5b4516d 100644 --- a/core/java/android/database/sqlite/SQLiteProgram.java +++ b/core/java/android/database/sqlite/SQLiteProgram.java @@ -61,6 +61,7 @@ public abstract class SQLiteProgram extends SQLiteClosable { // only cache CRUD statements String prefixSql = mSql.substring(0, 6); if (!prefixSql.equalsIgnoreCase("INSERT") && !prefixSql.equalsIgnoreCase("UPDATE") && + !prefixSql.equalsIgnoreCase("REPLAC") && !prefixSql.equalsIgnoreCase("DELETE") && !prefixSql.equalsIgnoreCase("SELECT")) { mCompiledSql = new SQLiteCompiledSql(db, sql); nStatement = mCompiledSql.nStatement; diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java index 85d52ec..3484d55 100644 --- a/core/java/android/hardware/Camera.java +++ b/core/java/android/hardware/Camera.java @@ -261,7 +261,6 @@ public class Camera { * setPreviewCallback, or to this method with a null callback parameter. * * @param cb A callback object that receives a copy of the preview frame. A null value will clear the queue. - * @hide */ public final void setPreviewCallbackWithBuffer(PreviewCallback cb) { mPreviewCallback = cb; @@ -271,16 +270,22 @@ public class Camera { } /** - * Adds a pre-allocated buffer to the callback buffer queue. + * Adds a pre-allocated buffer to the callback buffer queue. Applications + * can add one or more buffers to the queue. When a preview frame arrives + * and there is still available buffer, buffer will be filled and it is + * removed from the queue. Then preview callback is invoked with the buffer. + * If a frame arrives and there is no buffer left, the frame is discarded. + * Applications should add the buffers back when they finish the processing. + * * Preview width and height can be determined from getPreviewSize, and bitsPerPixel can be - * found from from {@link android.hardware.Camera.Parameters#getPreviewFormat()} and - * {@link android.graphics.ImageFormat#getBitsPerPixel(int)} + * found from {@link android.hardware.Camera.Parameters#getPreviewFormat()} + * and {@link android.graphics.ImageFormat#getBitsPerPixel(int)}. * * Alternatively, a buffer from a previous callback may be passed in or used * to determine the size of new preview frame buffers. * * @param callbackBuffer The buffer to register. Size should be width * height * bitsPerPixel / 8. - * @hide + * @see #setPreviewCallbackWithBuffer(PreviewCallback) */ public native final void addCallbackBuffer(byte[] callbackBuffer); diff --git a/core/java/android/os/Power.java b/core/java/android/os/Power.java index b3df522..5a79215 100644 --- a/core/java/android/os/Power.java +++ b/core/java/android/os/Power.java @@ -18,7 +18,6 @@ package android.os; import java.io.IOException; import android.os.ServiceManager; -import android.os.storage.IMountService; /** * Class that provides access to some of the power management functions. @@ -101,15 +100,6 @@ public class Power */ public static void reboot(String reason) throws IOException { - IMountService mSvc = IMountService.Stub.asInterface( - ServiceManager.getService("mount")); - - if (mSvc != null) { - try { - mSvc.shutdown(); - } catch (Exception e) { - } - } rebootNative(reason); } diff --git a/core/java/android/os/storage/IMountService.aidl b/core/java/android/os/storage/IMountService.aidl index ad4cb10..75455ab 100644 --- a/core/java/android/os/storage/IMountService.aidl +++ b/core/java/android/os/storage/IMountService.aidl @@ -18,6 +18,7 @@ package android.os.storage; import android.os.storage.IMountServiceListener; +import android.os.storage.IMountShutdownObserver; /** WARNING! Update IMountService.h and IMountService.cpp if you change this file. * In particular, the ordering of the methods below must match the @@ -142,6 +143,7 @@ interface IMountService /** * Shuts down the MountService and gracefully unmounts all external media. + * Invokes call back once the shutdown is complete. */ - void shutdown(); + void shutdown(IMountShutdownObserver observer); } diff --git a/core/java/android/os/storage/IMountShutdownObserver.aidl b/core/java/android/os/storage/IMountShutdownObserver.aidl new file mode 100644 index 0000000..0aa8a45 --- /dev/null +++ b/core/java/android/os/storage/IMountShutdownObserver.aidl @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.os.storage; + +/** + * Callback class for receiving events related + * to shutdown. + * + * @hide - For internal consumption only. + */ +interface IMountShutdownObserver { + /** + * This method is called when the shutdown + * of MountService completed. + * @param statusCode indicates success or failure + * of the shutdown. + */ + void onShutDownComplete(int statusCode); +} diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index fdde591..1483f13 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -1708,6 +1708,7 @@ public final class Settings { VOLUME_ALARM + APPEND_FOR_LAST_AUDIBLE, VOLUME_NOTIFICATION + APPEND_FOR_LAST_AUDIBLE, VOLUME_BLUETOOTH_SCO + APPEND_FOR_LAST_AUDIBLE, + VIBRATE_IN_SILENT, TEXT_AUTO_REPLACE, TEXT_AUTO_CAPS, TEXT_AUTO_PUNCTUATE, diff --git a/core/java/android/server/BluetoothEventLoop.java b/core/java/android/server/BluetoothEventLoop.java index f363828..c0e4600 100644 --- a/core/java/android/server/BluetoothEventLoop.java +++ b/core/java/android/server/BluetoothEventLoop.java @@ -374,6 +374,11 @@ class BluetoothEventLoop { Intent intent = null; if (propValues[1].equals("true")) { intent = new Intent(BluetoothDevice.ACTION_ACL_CONNECTED); + // Set the link timeout to 8000 slots (5 sec timeout) + // for bluetooth docks. + if (mBluetoothService.isBluetoothDock(address)) { + mBluetoothService.setLinkTimeout(address, 8000); + } } else { intent = new Intent(BluetoothDevice.ACTION_ACL_DISCONNECTED); } diff --git a/core/java/android/server/BluetoothService.java b/core/java/android/server/BluetoothService.java index e7d9d5b..2c73416 100644 --- a/core/java/android/server/BluetoothService.java +++ b/core/java/android/server/BluetoothService.java @@ -1910,6 +1910,13 @@ public class BluetoothService extends IBluetooth.Stub { return path; } + /*package */ void setLinkTimeout(String address, int num_slots) { + String path = getObjectPathFromAddress(address); + boolean result = setLinkTimeoutNative(path, num_slots); + + if (!result) log("Set Link Timeout to:" + num_slots + " slots failed"); + } + private static void log(String msg) { Log.d(TAG, msg); } @@ -1953,4 +1960,5 @@ public class BluetoothService extends IBluetooth.Stub { private native int addRfcommServiceRecordNative(String name, long uuidMsb, long uuidLsb, short channel); private native boolean removeServiceRecordNative(int handle); + private native boolean setLinkTimeoutNative(String path, int num_slots); } diff --git a/core/java/android/speech/tts/TextToSpeech.java b/core/java/android/speech/tts/TextToSpeech.java index 9e04cf8..80b3a74 100755 --- a/core/java/android/speech/tts/TextToSpeech.java +++ b/core/java/android/speech/tts/TextToSpeech.java @@ -1211,9 +1211,8 @@ public class TextToSpeech { mCachedParams[Engine.PARAM_POSITION_ENGINE + 1] = extra; } } - if (mITts.synthesizeToFile(mPackageName, text, mCachedParams, filename)){ - result = SUCCESS; - } + result = mITts.synthesizeToFile(mPackageName, text, mCachedParams, filename) ? + SUCCESS : ERROR; } catch (RemoteException e) { // TTS died; restart it. Log.e("TextToSpeech.java - synthesizeToFile", "RemoteException"); diff --git a/core/java/android/view/VelocityTracker.java b/core/java/android/view/VelocityTracker.java index c17a724..aab76c4 100644 --- a/core/java/android/view/VelocityTracker.java +++ b/core/java/android/view/VelocityTracker.java @@ -259,8 +259,6 @@ public final class VelocityTracker implements Poolable<VelocityTracker> { * * @param id Which pointer's velocity to return. * @return The previously computed X velocity. - * - * @hide Pending API approval */ public float getXVelocity(int id) { return mXVelocity[id]; @@ -272,8 +270,6 @@ public final class VelocityTracker implements Poolable<VelocityTracker> { * * @param id Which pointer's velocity to return. * @return The previously computed Y velocity. - * - * @hide Pending API approval */ public float getYVelocity(int id) { return mYVelocity[id]; diff --git a/core/java/android/view/ViewConfiguration.java b/core/java/android/view/ViewConfiguration.java index 2344c42..2b78d2d 100644 --- 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 attempting a paged scroll + * (in dips) + */ + private static final int PAGING_TOUCH_SLOP = TOUCH_SLOP * 3; + + /** * Distance between the first touch and second touch to still be considered a double tap */ private static final int DOUBLE_TAP_SLOP = 100; @@ -140,6 +146,7 @@ public class ViewConfiguration { private final int mMaximumFlingVelocity; private final int mScrollbarSize; private final int mTouchSlop; + private final int mPagingTouchSlop; private final int mDoubleTapSlop; private final int mWindowTouchSlop; private final int mMaximumDrawingCacheSize; @@ -158,6 +165,7 @@ public class ViewConfiguration { mMaximumFlingVelocity = MAXIMUM_FLING_VELOCITY; mScrollbarSize = SCROLL_BAR_SIZE; mTouchSlop = TOUCH_SLOP; + mPagingTouchSlop = PAGING_TOUCH_SLOP; mDoubleTapSlop = DOUBLE_TAP_SLOP; mWindowTouchSlop = WINDOW_TOUCH_SLOP; //noinspection deprecation @@ -184,6 +192,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); + mPagingTouchSlop = (int) (density * PAGING_TOUCH_SLOP + 0.5f); mDoubleTapSlop = (int) (density * DOUBLE_TAP_SLOP + 0.5f); mWindowTouchSlop = (int) (density * WINDOW_TOUCH_SLOP + 0.5f); @@ -339,6 +348,14 @@ public class ViewConfiguration { public int getScaledTouchSlop() { return mTouchSlop; } + + /** + * @return Distance a touch can wander before we think the user is scrolling a full page + * in dips + */ + public int getScaledPagingTouchSlop() { + return mPagingTouchSlop; + } /** * @return Distance between the first touch and second touch to still be diff --git a/core/java/android/webkit/LoadListener.java b/core/java/android/webkit/LoadListener.java index 115499f..7da99ca 100644 --- a/core/java/android/webkit/LoadListener.java +++ b/core/java/android/webkit/LoadListener.java @@ -1057,7 +1057,7 @@ class LoadListener extends Handler implements EventHandler { mCacheLoader != null) ? HTTP_OK : mStatusCode; // pass content-type content-length and content-encoding final int nativeResponse = nativeCreateResponse( - mUrl, statusCode, mStatusText, + originalUrl(), statusCode, mStatusText, mMimeType, mContentLength, mEncoding); if (mHeaders != null) { mHeaders.getHeaders(new Headers.HeaderCallback() { @@ -1256,9 +1256,6 @@ class LoadListener extends Handler implements EventHandler { return; } - if (mOriginalUrl == null) { - mOriginalUrl = mUrl; - } // Cache the redirect response if (getErrorID() == OK) { @@ -1273,6 +1270,8 @@ class LoadListener extends Handler implements EventHandler { WebViewWorker.MSG_REMOVE_CACHE, this).sendToTarget(); } + // Saving a copy of the unstripped url for the response + mOriginalUrl = redirectTo; // This will strip the anchor setUrl(redirectTo); diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java index 8981419..fb15f78 100644 --- a/core/java/android/webkit/WebSettings.java +++ b/core/java/android/webkit/WebSettings.java @@ -192,6 +192,7 @@ public class WebSettings { private boolean mBuiltInZoomControls = false; private boolean mAllowFileAccess = true; private boolean mLoadWithOverviewMode = false; + private boolean mUseSystemOverscrollBackground = false; // private WebSettings, not accessible by the host activity static private int mDoubleTapToastCount = 3; @@ -470,6 +471,23 @@ public class WebSettings { } /** + * Set whether the WebView uses system background for over scroll + * background. If false, it will use the WebView's background. Default is + * false. + */ + public void setUseSystemOverscrollBackground(boolean system) { + mUseSystemOverscrollBackground = system; + } + + /** + * Returns true if this WebView uses system background instead of WebView + * background for over scroll background. + */ + public boolean getUseSystemOverscrollBackground() { + return mUseSystemOverscrollBackground; + } + + /** * Store whether the WebView is saving form data. */ public void setSaveFormData(boolean save) { diff --git a/core/java/android/webkit/WebTextView.java b/core/java/android/webkit/WebTextView.java index 39edcad..016f016 100644 --- a/core/java/android/webkit/WebTextView.java +++ b/core/java/android/webkit/WebTextView.java @@ -377,11 +377,17 @@ import java.util.ArrayList; return; } mPreChange = postChange; - // This was simply a delete or a cut, so just delete the selection. - if (before > 0 && 0 == count) { - mWebView.deleteSelection(start, start + before); - // For this and all changes to the text, update our cache - updateCachedTextfield(); + if (0 == count) { + if (before > 0) { + // This was simply a delete or a cut, so just delete the + // selection. + mWebView.deleteSelection(start, start + before); + // For this and all changes to the text, update our cache + updateCachedTextfield(); + } + // before should never be negative, so whether it was a cut + // (handled above), or before is 0, in which case nothing has + // changed, we should return. return; } // Find the last character being replaced. If it can be represented by diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java index 6079a37..6f2d070 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -68,6 +68,7 @@ import android.view.inputmethod.InputConnection; import android.view.inputmethod.InputMethodManager; import android.webkit.WebTextView.AutoCompleteAdapter; import android.webkit.WebViewCore.EventHub; +import android.webkit.WebViewCore.TouchEventData; import android.widget.AbsoluteLayout; import android.widget.Adapter; import android.widget.AdapterView; @@ -375,27 +376,29 @@ public class WebView extends AbsoluteLayout // Whether to forward the touch events to WebCore private boolean mForwardTouchEvents = false; - // Whether to prevent drag during touch. The initial value depends on - // mForwardTouchEvents. If WebCore wants touch events, we assume it will - // take control of touch events unless it says no for touch down event. - private static final int PREVENT_DRAG_NO = 0; - private static final int PREVENT_DRAG_MAYBE_YES = 1; - private static final int PREVENT_DRAG_YES = 2; - private static final int PREVENT_DRAG_CANCEL = 3; - private int mPreventDrag = PREVENT_DRAG_NO; - - // by default mPreventLongPress is false. If it is true, long press event - // will be handled by WebKit instead of UI. - private boolean mPreventLongPress = false; - // by default mPreventDoubleTap is false. If it is true, double tap event - // will be handled by WebKit instead of UI. - private boolean mPreventDoubleTap = false; - - // this needs to be in sync with the logic in WebKit's - // EventHandler::handleTouchEvent() - private static final int TOUCH_PREVENT_DRAG = 0x1; - private static final int TOUCH_PREVENT_LONGPRESS = 0x2; - private static final int TOUCH_PREVENT_DOUBLETAP = 0x4; + // Whether to prevent default during touch. The initial value depends on + // mForwardTouchEvents. If WebCore wants all the touch events, it says yes + // for touch down. Otherwise UI will wait for the answer of the first + // confirmed move before taking over the control. + private static final int PREVENT_DEFAULT_NO = 0; + private static final int PREVENT_DEFAULT_MAYBE_YES = 1; + private static final int PREVENT_DEFAULT_NO_FROM_TOUCH_DOWN = 2; + private static final int PREVENT_DEFAULT_YES = 3; + private static final int PREVENT_DEFAULT_IGNORE = 4; + private int mPreventDefault = PREVENT_DEFAULT_IGNORE; + + // true when the touch movement exceeds the slop + private boolean mConfirmMove; + + // if true, touch events will be first processed by WebCore, if prevent + // default is not set, the UI will continue handle them. + private boolean mDeferTouchProcess; + + // to avoid interfering with the current touch events, track them + // separately. Currently no snapping or fling in the deferred process mode + private int mDeferTouchMode = TOUCH_DONE_MODE; + private float mLastDeferTouchX; + private float mLastDeferTouchY; // To keep track of whether the current drag was initiated by a WebTextView, // so that we know not to hide the cursor @@ -484,7 +487,7 @@ public class WebView extends AbsoluteLayout private int mAnchorX; private int mAnchorY; - /** + /* * Private message ids */ private static final int REMEMBER_PASSWORD = 1; @@ -496,39 +499,49 @@ public class WebView extends AbsoluteLayout private static final int RESUME_WEBCORE_PRIORITY = 7; private static final int DRAG_HELD_MOTIONLESS = 8; private static final int AWAKEN_SCROLL_BARS = 9; + private static final int PREVENT_DEFAULT_TIMEOUT = 10; + + private static final int FIRST_PRIVATE_MSG_ID = REMEMBER_PASSWORD; + private static final int LAST_PRIVATE_MSG_ID = PREVENT_DEFAULT_TIMEOUT; + /* + * Package message ids + */ //! arg1=x, arg2=y - static final int SCROLL_TO_MSG_ID = 10; - static final int SCROLL_BY_MSG_ID = 11; + static final int SCROLL_TO_MSG_ID = 101; + static final int SCROLL_BY_MSG_ID = 102; //! arg1=x, arg2=y - static final int SPAWN_SCROLL_TO_MSG_ID = 12; + static final int SPAWN_SCROLL_TO_MSG_ID = 103; //! arg1=x, arg2=y - static final int SYNC_SCROLL_TO_MSG_ID = 13; - static final int NEW_PICTURE_MSG_ID = 14; - static final int UPDATE_TEXT_ENTRY_MSG_ID = 15; - static final int WEBCORE_INITIALIZED_MSG_ID = 16; - static final int UPDATE_TEXTFIELD_TEXT_MSG_ID = 17; - static final int UPDATE_ZOOM_RANGE = 18; - static final int MOVE_OUT_OF_PLUGIN = 19; - static final int CLEAR_TEXT_ENTRY = 20; - static final int UPDATE_TEXT_SELECTION_MSG_ID = 21; - static final int SHOW_RECT_MSG_ID = 22; - static final int LONG_PRESS_CENTER = 23; - static final int PREVENT_TOUCH_ID = 24; - static final int WEBCORE_NEED_TOUCH_EVENTS = 25; + static final int SYNC_SCROLL_TO_MSG_ID = 104; + static final int NEW_PICTURE_MSG_ID = 105; + static final int UPDATE_TEXT_ENTRY_MSG_ID = 106; + static final int WEBCORE_INITIALIZED_MSG_ID = 107; + static final int UPDATE_TEXTFIELD_TEXT_MSG_ID = 108; + static final int UPDATE_ZOOM_RANGE = 109; + static final int MOVE_OUT_OF_PLUGIN = 110; + static final int CLEAR_TEXT_ENTRY = 111; + static final int UPDATE_TEXT_SELECTION_MSG_ID = 112; + static final int SHOW_RECT_MSG_ID = 113; + static final int LONG_PRESS_CENTER = 114; + static final int PREVENT_TOUCH_ID = 115; + static final int WEBCORE_NEED_TOUCH_EVENTS = 116; // obj=Rect in doc coordinates - static final int INVAL_RECT_MSG_ID = 26; - static final int REQUEST_KEYBOARD = 27; - static final int DO_MOTION_UP = 28; - static final int SHOW_FULLSCREEN = 29; - static final int HIDE_FULLSCREEN = 30; - static final int DOM_FOCUS_CHANGED = 31; - static final int IMMEDIATE_REPAINT_MSG_ID = 32; - static final int SET_ROOT_LAYER_MSG_ID = 33; - static final int RETURN_LABEL = 34; - static final int FIND_AGAIN = 35; - - static final String[] HandlerDebugString = { + static final int INVAL_RECT_MSG_ID = 117; + static final int REQUEST_KEYBOARD = 118; + static final int DO_MOTION_UP = 119; + static final int SHOW_FULLSCREEN = 120; + static final int HIDE_FULLSCREEN = 121; + static final int DOM_FOCUS_CHANGED = 122; + static final int IMMEDIATE_REPAINT_MSG_ID = 123; + static final int SET_ROOT_LAYER_MSG_ID = 124; + static final int RETURN_LABEL = 125; + static final int FIND_AGAIN = 126; + + private static final int FIRST_PACKAGE_MSG_ID = SCROLL_TO_MSG_ID; + private static final int LAST_PACKAGE_MSG_ID = FIND_AGAIN; + + static final String[] HandlerPrivateDebugString = { "REMEMBER_PASSWORD", // = 1; "NEVER_REMEMBER_PASSWORD", // = 2; "SWITCH_TO_SHORTPRESS", // = 3; @@ -538,32 +551,36 @@ public class WebView extends AbsoluteLayout "RESUME_WEBCORE_PRIORITY", // = 7; "DRAG_HELD_MOTIONLESS", // = 8; "AWAKEN_SCROLL_BARS", // = 9; - "SCROLL_TO_MSG_ID", // = 10; - "SCROLL_BY_MSG_ID", // = 11; - "SPAWN_SCROLL_TO_MSG_ID", // = 12; - "SYNC_SCROLL_TO_MSG_ID", // = 13; - "NEW_PICTURE_MSG_ID", // = 14; - "UPDATE_TEXT_ENTRY_MSG_ID", // = 15; - "WEBCORE_INITIALIZED_MSG_ID", // = 16; - "UPDATE_TEXTFIELD_TEXT_MSG_ID", // = 17; - "UPDATE_ZOOM_RANGE", // = 18; - "MOVE_OUT_OF_PLUGIN", // = 19; - "CLEAR_TEXT_ENTRY", // = 20; - "UPDATE_TEXT_SELECTION_MSG_ID", // = 21; - "SHOW_RECT_MSG_ID", // = 22; - "LONG_PRESS_CENTER", // = 23; - "PREVENT_TOUCH_ID", // = 24; - "WEBCORE_NEED_TOUCH_EVENTS", // = 25; - "INVAL_RECT_MSG_ID", // = 26; - "REQUEST_KEYBOARD", // = 27; - "DO_MOTION_UP", // = 28; - "SHOW_FULLSCREEN", // = 29; - "HIDE_FULLSCREEN", // = 30; - "DOM_FOCUS_CHANGED", // = 31; - "IMMEDIATE_REPAINT_MSG_ID", // = 32; - "SET_ROOT_LAYER_MSG_ID", // = 33; - "RETURN_LABEL", // = 34; - "FIND_AGAIN" // = 35; + "PREVENT_DEFAULT_TIMEOUT" // = 10; + }; + + static final String[] HandlerPackageDebugString = { + "SCROLL_TO_MSG_ID", // = 101; + "SCROLL_BY_MSG_ID", // = 102; + "SPAWN_SCROLL_TO_MSG_ID", // = 103; + "SYNC_SCROLL_TO_MSG_ID", // = 104; + "NEW_PICTURE_MSG_ID", // = 105; + "UPDATE_TEXT_ENTRY_MSG_ID", // = 106; + "WEBCORE_INITIALIZED_MSG_ID", // = 107; + "UPDATE_TEXTFIELD_TEXT_MSG_ID", // = 108; + "UPDATE_ZOOM_RANGE", // = 109; + "MOVE_OUT_OF_PLUGIN", // = 110; + "CLEAR_TEXT_ENTRY", // = 111; + "UPDATE_TEXT_SELECTION_MSG_ID", // = 112; + "SHOW_RECT_MSG_ID", // = 113; + "LONG_PRESS_CENTER", // = 114; + "PREVENT_TOUCH_ID", // = 115; + "WEBCORE_NEED_TOUCH_EVENTS", // = 116; + "INVAL_RECT_MSG_ID", // = 117; + "REQUEST_KEYBOARD", // = 118; + "DO_MOTION_UP", // = 119; + "SHOW_FULLSCREEN", // = 120; + "HIDE_FULLSCREEN", // = 121; + "DOM_FOCUS_CHANGED", // = 122; + "IMMEDIATE_REPAINT_MSG_ID", // = 123; + "SET_ROOT_LAYER_MSG_ID", // = 124; + "RETURN_LABEL", // = 125; + "FIND_AGAIN" // = 126; }; // If the site doesn't use the viewport meta tag to specify the viewport, @@ -1125,9 +1142,11 @@ public class WebView extends AbsoluteLayout mCallbackProxy.setWebViewClient(null); mCallbackProxy.setWebChromeClient(null); // Tell WebViewCore to destroy itself - WebViewCore webViewCore = mWebViewCore; - mWebViewCore = null; // prevent using partial webViewCore - webViewCore.destroy(); + synchronized (this) { + WebViewCore webViewCore = mWebViewCore; + mWebViewCore = null; // prevent using partial webViewCore + webViewCore.destroy(); + } // Remove any pending messages that might not be serviced yet. mPrivateHandler.removeCallbacksAndMessages(null); mCallbackProxy.removeCallbacksAndMessages(null); @@ -2180,14 +2199,15 @@ public class WebView extends AbsoluteLayout // Sets r to be our visible rectangle in content coordinates private void calcOurContentVisibleRect(Rect r) { calcOurVisibleRect(r); - r.left = viewToContentX(r.left); + // since we might overscroll, pin the rect to the bounds of the content + r.left = Math.max(viewToContentX(r.left), 0); // viewToContentY will remove the total height of the title bar. Add // the visible height back in to account for the fact that if the title // bar is partially visible, the part of the visible rect which is // displaying our content is displaced by that amount. - r.top = viewToContentY(r.top + getVisibleTitleHeight()); - r.right = viewToContentX(r.right); - r.bottom = viewToContentY(r.bottom); + r.top = Math.max(viewToContentY(r.top + getVisibleTitleHeight()), 0); + r.right = Math.min(viewToContentX(r.right), mContentWidth); + r.bottom = Math.min(viewToContentY(r.bottom), mContentHeight); } static class ViewSizeData { @@ -2286,9 +2306,11 @@ public class WebView extends AbsoluteLayout boolean clampedY) { mInOverScrollMode = false; int maxX = computeMaxScrollX(); - if (Math.abs(mMinZoomScale - mMaxZoomScale) < 0.01f && maxX == 0) { - // do not over scroll x if the page can't be zoomed and it just fits - // the screen + if (maxX == 0 && (Math.abs(mMinZoomScale - mMaxZoomScale) < 0.01f) + || !getSettings().supportZoom() + || !getSettings().getUseWideViewPort()) { + // do not over scroll x if the page just fits the screen and it + // can't zoom or the view doesn't use wide viewport scrollX = pinLocX(scrollX); } else if (scrollX < 0 || scrollX > maxX) { mInOverScrollMode = true; @@ -3093,7 +3115,8 @@ public class WebView extends AbsoluteLayout } int saveCount = canvas.save(); - if (mInOverScrollMode) { + if (mInOverScrollMode + && getSettings().getUseSystemOverscrollBackground()) { if (mOverScrollBackground == null) { mOverScrollBackground = new Paint(); Bitmap bm = BitmapFactory.decodeResource( @@ -3236,10 +3259,11 @@ public class WebView extends AbsoluteLayout } boolean animateZoom = mZoomScale != 0; - boolean animateScroll = (!mScroller.isFinished() + boolean animateScroll = ((!mScroller.isFinished() || mVelocityTracker != null) && (mTouchMode != TOUCH_DRAG_MODE || - mHeldMotionless != MOTIONLESS_TRUE); + mHeldMotionless != MOTIONLESS_TRUE)) + || mDeferTouchMode == TOUCH_DRAG_MODE; if (mTouchMode == TOUCH_DRAG_MODE) { if (mHeldMotionless == MOTIONLESS_PENDING) { mPrivateHandler.removeMessages(DRAG_HELD_MOTIONLESS); @@ -3336,11 +3360,9 @@ public class WebView extends AbsoluteLayout if (mTouchMode == TOUCH_SHORTPRESS_START_MODE) { mTouchMode = TOUCH_SHORTPRESS_MODE; HitTestResult hitTest = getHitTestResult(); - if (mPreventLongPress || (hitTest != null && - hitTest.mType != HitTestResult.UNKNOWN_TYPE)) { - mPrivateHandler.sendMessageDelayed(mPrivateHandler - .obtainMessage(SWITCH_TO_LONGPRESS), - LONG_PRESS_TIMEOUT); + if (hitTest == null + || hitTest.mType == HitTestResult.UNKNOWN_TYPE) { + mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS); } } } @@ -3673,11 +3695,13 @@ public class WebView extends AbsoluteLayout return false; } - if (mShiftIsPressed == false && nativeCursorWantsKeyEvents() == false - && !nativeFocusIsPlugin() - && (keyCode == KeyEvent.KEYCODE_SHIFT_LEFT - || keyCode == KeyEvent.KEYCODE_SHIFT_RIGHT)) { - setUpSelectXY(); + if (keyCode == KeyEvent.KEYCODE_SHIFT_LEFT + || keyCode == KeyEvent.KEYCODE_SHIFT_RIGHT) { + if (nativeFocusIsPlugin()) { + mShiftIsPressed = true; + } else if (!nativeCursorWantsKeyEvents() && !mShiftIsPressed) { + setUpSelectXY(); + } } if (keyCode >= KeyEvent.KEYCODE_DPAD_UP @@ -3816,7 +3840,10 @@ public class WebView extends AbsoluteLayout if (keyCode == KeyEvent.KEYCODE_SHIFT_LEFT || keyCode == KeyEvent.KEYCODE_SHIFT_RIGHT) { - if (commitCopy()) { + if (nativeFocusIsPlugin()) { + mShiftIsPressed = false; + return true; + } else if (commitCopy()) { return true; } } @@ -4177,7 +4204,9 @@ public class WebView extends AbsoluteLayout public boolean dispatchKeyEvent(KeyEvent event) { boolean dispatch = true; - if (!inEditingMode()) { + // Textfields and plugins need to receive the shift up key even if + // another key was released while the shift key was held down. + if (!inEditingMode() && !nativeFocusIsPlugin()) { if (event.getAction() == KeyEvent.ACTION_DOWN) { mGotKeyDown = true; } else { @@ -4456,6 +4485,7 @@ public class WebView extends AbsoluteLayout // may trigger the unwanted click, can't use TOUCH_DRAG_MODE as it // may trigger the unwanted fling. mTouchMode = TOUCH_PINCH_DRAG; + mConfirmMove = true; startTouch(detector.getFocusX(), detector.getFocusY(), mLastTouchTime); } @@ -4481,6 +4511,22 @@ public class WebView extends AbsoluteLayout } } + private boolean hitFocusedPlugin(int contentX, int contentY) { + return nativeFocusIsPlugin() + && nativePointInNavCache(contentX, contentY, mNavSlop) + && nativeCacheHitNodePointer() == nativeFocusNodePointer(); + } + + private boolean shouldForwardTouchEvent() { + return mFullScreenHolder != null || (mForwardTouchEvents + && mTouchMode != TOUCH_SELECT_MODE + && mPreventDefault != PREVENT_DEFAULT_IGNORE); + } + + private boolean inFullScreenMode() { + return mFullScreenHolder != null; + } + @Override public boolean onTouchEvent(MotionEvent ev) { if (mNativeClass == 0 || !isClickable() || !isLongClickable()) { @@ -4542,46 +4588,33 @@ public class WebView extends AbsoluteLayout y = getViewHeightWithTitle() - 1; } - // pass the touch events, except ACTION_MOVE which will be handled - // later, from UI thread to WebCore thread - if (mFullScreenHolder != null || (mForwardTouchEvents - && action != MotionEvent.ACTION_MOVE - && (action == MotionEvent.ACTION_DOWN || mPreventDrag - != PREVENT_DRAG_CANCEL))) { - WebViewCore.TouchEventData ted = new WebViewCore.TouchEventData(); - ted.mAction = action; - ted.mX = viewToContentX((int) x + mScrollX); - ted.mY = viewToContentY((int) y + mScrollY); - ted.mEventTime = eventTime; - ted.mMetaState = ev.getMetaState(); - mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted); - mLastSentTouchTime = eventTime; - } - float fDeltaX = mLastTouchX - x; float fDeltaY = mLastTouchY - y; int deltaX = (int) fDeltaX; int deltaY = (int) fDeltaY; + int contentX = viewToContentX((int) x + mScrollX); + int contentY = viewToContentY((int) y + mScrollY); switch (action) { case MotionEvent.ACTION_DOWN: { - mPreventDrag = PREVENT_DRAG_NO; + mPreventDefault = PREVENT_DEFAULT_NO; + mConfirmMove = false; if (!mScroller.isFinished()) { // stop the current scroll animation, but if this is // the start of a fling, allow it to add to the current // fling's velocity mScroller.abortAnimation(); mTouchMode = TOUCH_DRAG_START_MODE; + mConfirmMove = true; mPrivateHandler.removeMessages(RESUME_WEBCORE_PRIORITY); - } else if (mShiftIsPressed) { + } else if (!inFullScreenMode() && mShiftIsPressed) { mSelectX = mScrollX + (int) x; mSelectY = mScrollY + (int) y; mTouchMode = TOUCH_SELECT_MODE; if (DebugFlags.WEB_VIEW) { Log.v(LOGTAG, "select=" + mSelectX + "," + mSelectY); } - nativeMoveSelection(viewToContentX(mSelectX), - viewToContentY(mSelectY), false); + nativeMoveSelection(contentX, contentY, false); mTouchSelection = mExtendSelection = true; invalidate(); // draw the i-beam instead of the arrow } else if (mPrivateHandler.hasMessages(RELEASE_SINGLE_TAP)) { @@ -4591,15 +4624,17 @@ public class WebView extends AbsoluteLayout } else { // commit the short press action for the previous tap doShortPress(); - // continue, mTouchMode should be still TOUCH_INIT_MODE + mTouchMode = TOUCH_INIT_MODE; + mDeferTouchProcess = (!inFullScreenMode() + && mForwardTouchEvents) ? hitFocusedPlugin( + contentX, contentY) : false; } - } else { + } else { // the normal case mPreviewZoomOnly = false; mTouchMode = TOUCH_INIT_MODE; - mPreventDrag = mForwardTouchEvents ? PREVENT_DRAG_MAYBE_YES - : PREVENT_DRAG_NO; - mPreventLongPress = false; - mPreventDoubleTap = false; + mDeferTouchProcess = (!inFullScreenMode() + && mForwardTouchEvents) ? hitFocusedPlugin( + contentX, contentY) : false; mWebViewCore.sendMessage( EventHub.UPDATE_FRAME_CACHE_IF_LOADING); if (mLogEvent && eventTime - mLastTouchUpTime < 1000) { @@ -4610,19 +4645,87 @@ public class WebView extends AbsoluteLayout // Trigger the link if (mTouchMode == TOUCH_INIT_MODE || mTouchMode == TOUCH_DOUBLE_TAP_MODE) { - mPrivateHandler.sendMessageDelayed(mPrivateHandler - .obtainMessage(SWITCH_TO_SHORTPRESS), TAP_TIMEOUT); + mPrivateHandler.sendEmptyMessageDelayed( + SWITCH_TO_SHORTPRESS, TAP_TIMEOUT); + mPrivateHandler.sendEmptyMessageDelayed( + SWITCH_TO_LONGPRESS, LONG_PRESS_TIMEOUT); + if (inFullScreenMode() || mDeferTouchProcess) { + mPreventDefault = PREVENT_DEFAULT_YES; + } else if (mForwardTouchEvents) { + mPreventDefault = PREVENT_DEFAULT_MAYBE_YES; + } else { + mPreventDefault = PREVENT_DEFAULT_NO; + } + // pass the touch events from UI thread to WebCore thread + if (shouldForwardTouchEvent()) { + TouchEventData ted = new TouchEventData(); + ted.mAction = action; + ted.mX = contentX; + ted.mY = contentY; + ted.mMetaState = ev.getMetaState(); + ted.mReprocess = mDeferTouchProcess; + mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted); + if (mDeferTouchProcess) { + // still needs to set them for compute deltaX/Y + mLastTouchX = x; + mLastTouchY = y; + break; + } + if (!inFullScreenMode()) { + mPrivateHandler.sendMessageDelayed(mPrivateHandler + .obtainMessage(PREVENT_DEFAULT_TIMEOUT, + action, 0), TAP_TIMEOUT); + } + } } startTouch(x, y, eventTime); break; } case MotionEvent.ACTION_MOVE: { - if (mTouchMode == TOUCH_DONE_MODE) { - // no dragging during scroll zoom animation + boolean firstMove = false; + if (!mConfirmMove && (deltaX * deltaX + deltaY * deltaY) + >= mTouchSlopSquare) { + mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS); + mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS); + mConfirmMove = true; + firstMove = true; + if (mTouchMode == TOUCH_DOUBLE_TAP_MODE) { + mTouchMode = TOUCH_INIT_MODE; + } + } + // pass the touch events from UI thread to WebCore thread + if (shouldForwardTouchEvent() && mConfirmMove && (firstMove + || eventTime - mLastSentTouchTime > mCurrentTouchInterval)) { + TouchEventData ted = new TouchEventData(); + ted.mAction = action; + ted.mX = contentX; + ted.mY = contentY; + ted.mMetaState = ev.getMetaState(); + ted.mReprocess = mDeferTouchProcess; + mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted); + mLastSentTouchTime = eventTime; + if (mDeferTouchProcess) { + break; + } + if (firstMove && !inFullScreenMode()) { + mPrivateHandler.sendMessageDelayed(mPrivateHandler + .obtainMessage(PREVENT_DEFAULT_TIMEOUT, + action, 0), TAP_TIMEOUT); + } + } + if (mTouchMode == TOUCH_DONE_MODE + || mPreventDefault == PREVENT_DEFAULT_YES) { + // no dragging during scroll zoom animation, or when prevent + // default is yes break; } + if (mVelocityTracker == null) { + Log.e(LOGTAG, "Got null mVelocityTracker when " + + "mPreventDefault = " + mPreventDefault + + " mDeferTouchProcess = " + mDeferTouchProcess + + " mTouchMode = " + mTouchMode); + } mVelocityTracker.addMovement(ev); - if (mTouchMode != TOUCH_DRAG_MODE) { if (mTouchMode == TOUCH_SELECT_MODE) { mSelectX = mScrollX + (int) x; @@ -4630,48 +4733,20 @@ public class WebView extends AbsoluteLayout if (DebugFlags.WEB_VIEW) { Log.v(LOGTAG, "xtend=" + mSelectX + "," + mSelectY); } - nativeMoveSelection(viewToContentX(mSelectX), - viewToContentY(mSelectY), true); + nativeMoveSelection(contentX, contentY, true); invalidate(); break; } - if ((deltaX * deltaX + deltaY * deltaY) < mTouchSlopSquare) { + if (!mConfirmMove) { break; } - - // pass the first ACTION_MOVE from UI thread to WebCore - // thread after the distance is confirmed that it is a drag - if (mFullScreenHolder == null && mForwardTouchEvents - && mPreventDrag != PREVENT_DRAG_CANCEL) { - WebViewCore.TouchEventData ted = new WebViewCore.TouchEventData(); - ted.mAction = action; - ted.mX = viewToContentX((int) x + mScrollX); - ted.mY = viewToContentY((int) y + mScrollY); - ted.mEventTime = eventTime; - ted.mMetaState = ev.getMetaState(); - mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted); - mLastSentTouchTime = eventTime; - } - - if (mPreventDrag == PREVENT_DRAG_MAYBE_YES) { + if (mPreventDefault == PREVENT_DEFAULT_MAYBE_YES + || mPreventDefault == PREVENT_DEFAULT_NO_FROM_TOUCH_DOWN) { // track mLastTouchTime as we may need to do fling at // ACTION_UP mLastTouchTime = eventTime; break; } - if (mTouchMode == TOUCH_SHORTPRESS_MODE - || mTouchMode == TOUCH_SHORTPRESS_START_MODE) { - mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS); - } else if (mTouchMode == TOUCH_INIT_MODE - || mTouchMode == TOUCH_DOUBLE_TAP_MODE) { - mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS); - } - if (mFullScreenHolder != null) { - // in full screen mode, the WebView can't be panned. - mTouchMode = TOUCH_DONE_MODE; - break; - } - // if it starts nearly horizontal or vertical, enforce it int ax = Math.abs(deltaX); int ay = Math.abs(deltaY); @@ -4691,38 +4766,11 @@ public class WebView extends AbsoluteLayout deltaX = 0; deltaY = 0; - WebViewCore.reducePriority(); - if (!mDragFromTextInput) { - nativeHideCursor(); - } - WebSettings settings = getSettings(); - if (settings.supportZoom() - && settings.getBuiltInZoomControls() - && !mZoomButtonsController.isVisible() - && mMinZoomScale < mMaxZoomScale) { - mZoomButtonsController.setVisible(true); - int count = settings.getDoubleTapToastCount(); - if (mInZoomOverview && count > 0) { - settings.setDoubleTapToastCount(--count); - Toast.makeText(mContext, - com.android.internal.R.string.double_tap_toast, - Toast.LENGTH_LONG).show(); - } - } - } else { - // pass the touch events from UI thread to WebCore thread - if (mFullScreenHolder == null && mForwardTouchEvents - && eventTime - mLastSentTouchTime > mCurrentTouchInterval - && mPreventDrag != PREVENT_DRAG_CANCEL) { - WebViewCore.TouchEventData ted = new WebViewCore.TouchEventData(); - ted.mAction = action; - ted.mX = viewToContentX((int) x + mScrollX); - ted.mY = viewToContentY((int) y + mScrollY); - ted.mEventTime = eventTime; - ted.mMetaState = ev.getMetaState(); - mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted); - mLastSentTouchTime = eventTime; - } + startDrag(); + } + + if (mDragTrackerHandler != null) { + mDragTrackerHandler.dragTo(x, y); } // do pan @@ -4770,9 +4818,6 @@ public class WebView extends AbsoluteLayout } } if ((deltaX | deltaY) != 0) { - overscrollBy(deltaX, deltaY, mScrollX, mScrollY, - computeMaxScrollX(), computeMaxScrollY(), - getViewWidth() / 3, getViewHeight() / 3); if (deltaX != 0) { mLastTouchX = x; } @@ -4789,22 +4834,7 @@ public class WebView extends AbsoluteLayout mUserScroll = true; } - if (!getSettings().getBuiltInZoomControls()) { - boolean showPlusMinus = mMinZoomScale < mMaxZoomScale; - if (mZoomControls != null && showPlusMinus) { - if (mZoomControls.getVisibility() == View.VISIBLE) { - mPrivateHandler.removeCallbacks(mZoomControlRunnable); - } else { - mZoomControls.show(showPlusMinus, false); - } - mPrivateHandler.postDelayed(mZoomControlRunnable, - ZOOM_CONTROLS_TIMEOUT); - } - } - - if (mDragTrackerHandler != null) { - mDragTrackerHandler.dragTo(x, y); - } + doDrag(deltaX, deltaY); if (keepScrollBarsVisible) { if (mHeldMotionless != MOTIONLESS_TRUE) { @@ -4821,25 +4851,32 @@ public class WebView extends AbsoluteLayout break; } case MotionEvent.ACTION_UP: { - if (mDragTrackerHandler != null) { - mDragTrackerHandler.stopDrag(); + // pass the touch events from UI thread to WebCore thread + if (shouldForwardTouchEvent()) { + TouchEventData ted = new TouchEventData(); + ted.mAction = action; + ted.mX = contentX; + ted.mY = contentY; + ted.mMetaState = ev.getMetaState(); + ted.mReprocess = mDeferTouchProcess; + mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted); } mLastTouchUpTime = eventTime; switch (mTouchMode) { case TOUCH_DOUBLE_TAP_MODE: // double tap mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS); - mTouchMode = TOUCH_DONE_MODE; - if (mPreventDoubleTap) { - WebViewCore.TouchEventData ted - = new WebViewCore.TouchEventData(); + mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS); + if (inFullScreenMode() || mDeferTouchProcess) { + TouchEventData ted = new TouchEventData(); ted.mAction = WebViewCore.ACTION_DOUBLETAP; - ted.mX = viewToContentX((int) x + mScrollX); - ted.mY = viewToContentY((int) y + mScrollY); - ted.mEventTime = eventTime; + ted.mX = contentX; + ted.mY = contentY; ted.mMetaState = ev.getMetaState(); + ted.mReprocess = mDeferTouchProcess; mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted); - } else if (mFullScreenHolder == null) { + } else if (mPreventDefault != PREVENT_DEFAULT_YES){ doDoubleTap(); + mTouchMode = TOUCH_DONE_MODE; } break; case TOUCH_SELECT_MODE: @@ -4851,20 +4888,15 @@ public class WebView extends AbsoluteLayout case TOUCH_SHORTPRESS_MODE: mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS); mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS); - if ((deltaX * deltaX + deltaY * deltaY) > mTouchSlopSquare) { + if (mConfirmMove) { Log.w(LOGTAG, "Miss a drag as we are waiting for" + " WebCore's response for touch down."); - if (mFullScreenHolder == null + if (mPreventDefault != PREVENT_DEFAULT_YES && (computeMaxScrollX() > 0 || computeMaxScrollY() > 0)) { - // remove the pending TOUCH_EVENT and send a - // cancel - mWebViewCore - .removeMessages(EventHub.TOUCH_EVENT); - WebViewCore.TouchEventData ted = new WebViewCore.TouchEventData(); - ted.mAction = MotionEvent.ACTION_CANCEL; - mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, - ted); + // UI takes control back, cancel WebCore touch + cancelWebCoreTouchEvent(contentX, contentY, + true); // we will not rewrite drag code here, but we // will try fling if it applies. WebViewCore.reducePriority(); @@ -4873,20 +4905,12 @@ public class WebView extends AbsoluteLayout break; } } else { - // mPreventDrag can be PREVENT_DRAG_MAYBE_YES in - // TOUCH_INIT_MODE. To give WebCoreThread a little - // more time to send PREVENT_TOUCH_ID, we check - // again in responding RELEASE_SINGLE_TAP. - if (mPreventDrag != PREVENT_DRAG_YES) { - if (mTouchMode == TOUCH_INIT_MODE) { - mPrivateHandler.sendMessageDelayed( - mPrivateHandler.obtainMessage( - RELEASE_SINGLE_TAP), - ViewConfiguration.getDoubleTapTimeout()); - } else { - mTouchMode = TOUCH_DONE_MODE; - doShortPress(); - } + if (mTouchMode == TOUCH_INIT_MODE) { + mPrivateHandler.sendEmptyMessageDelayed( + RELEASE_SINGLE_TAP, ViewConfiguration + .getDoubleTapTimeout()); + } else { + doShortPress(); } break; } @@ -4899,6 +4923,13 @@ public class WebView extends AbsoluteLayout // if the user waits a while w/o moving before the // up, we don't want to do a fling if (eventTime - mLastTouchTime <= MIN_FLING_TIME) { + if (mVelocityTracker == null) { + Log.e(LOGTAG, "Got null mVelocityTracker when " + + "mPreventDefault = " + + mPreventDefault + + " mDeferTouchProcess = " + + mDeferTouchProcess); + } mVelocityTracker.addMovement(ev); doFling(); break; @@ -4912,34 +4943,38 @@ public class WebView extends AbsoluteLayout mLastVelocity = 0; WebViewCore.resumePriority(); break; - case TOUCH_DRAG_START_MODE: - case TOUCH_DONE_MODE: - // do nothing - break; - } - // we also use mVelocityTracker == null to tell us that we are - // not "moving around", so we can take the slower/prettier - // mode in the drawing code - if (mVelocityTracker != null) { - mVelocityTracker.recycle(); - mVelocityTracker = null; } + stopTouch(); break; } case MotionEvent.ACTION_CANCEL: { - cancelTouch(); if (mTouchMode == TOUCH_DRAG_MODE) { - if (mScroller.springback(mScrollX, mScrollY, 0, - computeMaxScrollX(), 0, computeMaxScrollY())) { - invalidate(); - } + mScroller.springback(mScrollX, mScrollY, 0, + computeMaxScrollX(), 0, computeMaxScrollY()); + invalidate(); } + cancelWebCoreTouchEvent(contentX, contentY, false); + cancelTouch(); break; } } return true; } + private void cancelWebCoreTouchEvent(int x, int y, boolean removeEvents) { + if (shouldForwardTouchEvent()) { + if (removeEvents) { + mWebViewCore.removeMessages(EventHub.TOUCH_EVENT); + } + TouchEventData ted = new TouchEventData(); + ted.mX = x; + ted.mY = y; + ted.mAction = MotionEvent.ACTION_CANCEL; + mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted); + mPreventDefault = PREVENT_DEFAULT_IGNORE; + } + } + private void startTouch(float x, float y, long eventTime) { // Remember where the motion event started mLastTouchX = x; @@ -4952,6 +4987,60 @@ public class WebView extends AbsoluteLayout } } + private void startDrag() { + WebViewCore.reducePriority(); + if (!mDragFromTextInput) { + nativeHideCursor(); + } + WebSettings settings = getSettings(); + if (settings.supportZoom() + && settings.getBuiltInZoomControls() + && !mZoomButtonsController.isVisible() + && mMinZoomScale < mMaxZoomScale) { + mZoomButtonsController.setVisible(true); + int count = settings.getDoubleTapToastCount(); + if (mInZoomOverview && count > 0) { + settings.setDoubleTapToastCount(--count); + Toast.makeText(mContext, + com.android.internal.R.string.double_tap_toast, + Toast.LENGTH_LONG).show(); + } + } + } + + private void doDrag(int deltaX, int deltaY) { + if ((deltaX | deltaY) != 0) { + overscrollBy(deltaX, deltaY, mScrollX, mScrollY, + computeMaxScrollX(), computeMaxScrollY(), + getViewWidth() / 3, getViewHeight() / 3); + } + if (!getSettings().getBuiltInZoomControls()) { + boolean showPlusMinus = mMinZoomScale < mMaxZoomScale; + if (mZoomControls != null && showPlusMinus) { + if (mZoomControls.getVisibility() == View.VISIBLE) { + mPrivateHandler.removeCallbacks(mZoomControlRunnable); + } else { + mZoomControls.show(showPlusMinus, false); + } + mPrivateHandler.postDelayed(mZoomControlRunnable, + ZOOM_CONTROLS_TIMEOUT); + } + } + } + + private void stopTouch() { + if (mDragTrackerHandler != null) { + mDragTrackerHandler.stopDrag(); + } + // we also use mVelocityTracker == null to tell us that we are + // not "moving around", so we can take the slower/prettier + // mode in the drawing code + if (mVelocityTracker != null) { + mVelocityTracker.recycle(); + mVelocityTracker = null; + } + } + private void cancelTouch() { if (mDragTrackerHandler != null) { mDragTrackerHandler.stopDrag(); @@ -5571,6 +5660,10 @@ public class WebView extends AbsoluteLayout if (mNativeClass == 0) { return; } + if (mPreventDefault == PREVENT_DEFAULT_YES) { + return; + } + mTouchMode = TOUCH_DONE_MODE; switchOutDrawHistory(); // mLastTouchX and mLastTouchY are the point in the current viewport int contentX = viewToContentX((int) mLastTouchX + mScrollX); @@ -5842,7 +5935,7 @@ public class WebView extends AbsoluteLayout cursorData(), 1000); } - /* package */ WebViewCore getWebViewCore() { + /* package */ synchronized WebViewCore getWebViewCore() { return mWebViewCore; } @@ -5859,9 +5952,17 @@ public class WebView extends AbsoluteLayout public void handleMessage(Message msg) { // exclude INVAL_RECT_MSG_ID since it is frequently output if (DebugFlags.WEB_VIEW && msg.what != INVAL_RECT_MSG_ID) { - Log.v(LOGTAG, msg.what < REMEMBER_PASSWORD || msg.what - > FIND_AGAIN ? Integer.toString(msg.what) - : HandlerDebugString[msg.what - REMEMBER_PASSWORD]); + if (msg.what >= FIRST_PRIVATE_MSG_ID + && msg.what <= LAST_PRIVATE_MSG_ID) { + Log.v(LOGTAG, HandlerPrivateDebugString[msg.what + - FIRST_PRIVATE_MSG_ID]); + } else if (msg.what >= FIRST_PACKAGE_MSG_ID + && msg.what <= LAST_PACKAGE_MSG_ID) { + Log.v(LOGTAG, HandlerPackageDebugString[msg.what + - FIRST_PACKAGE_MSG_ID]); + } else { + Log.v(LOGTAG, Integer.toString(msg.what)); + } } if (mWebViewCore == null) { // after WebView's destroy() is called, skip handling messages. @@ -5882,70 +5983,57 @@ public class WebView extends AbsoluteLayout ((Message) msg.obj).sendToTarget(); break; } - case SWITCH_TO_SHORTPRESS: { - // if mPreventDrag is not confirmed, cancel it so that it - // won't block panning the page. - if (mPreventDrag == PREVENT_DRAG_MAYBE_YES) { - mPreventDrag = PREVENT_DRAG_CANCEL; - mPreventLongPress = false; - mPreventDoubleTap = false; - // remove the pending TOUCH_EVENT and send a cancel - mWebViewCore.removeMessages(EventHub.TOUCH_EVENT); - WebViewCore.TouchEventData ted = new WebViewCore.TouchEventData(); - ted.mAction = MotionEvent.ACTION_CANCEL; - mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted); + case PREVENT_DEFAULT_TIMEOUT: { + // if timeout happens, cancel it so that it won't block UI + // to continue handling touch events + if ((msg.arg1 == MotionEvent.ACTION_DOWN + && mPreventDefault == PREVENT_DEFAULT_MAYBE_YES) + || (msg.arg1 == MotionEvent.ACTION_MOVE + && mPreventDefault == PREVENT_DEFAULT_NO_FROM_TOUCH_DOWN)) { + cancelWebCoreTouchEvent( + viewToContentX((int) mLastTouchX + mScrollX), + viewToContentY((int) mLastTouchY + mScrollY), + true); } + break; + } + case SWITCH_TO_SHORTPRESS: { if (mTouchMode == TOUCH_INIT_MODE) { - mTouchMode = mFullScreenHolder == null - ? TOUCH_SHORTPRESS_START_MODE - : TOUCH_SHORTPRESS_MODE; - updateSelection(); + if (mPreventDefault != PREVENT_DEFAULT_YES) { + mTouchMode = TOUCH_SHORTPRESS_START_MODE; + updateSelection(); + } else { + // set to TOUCH_SHORTPRESS_MODE so that it won't + // trigger double tap any more + mTouchMode = TOUCH_SHORTPRESS_MODE; + } } else if (mTouchMode == TOUCH_DOUBLE_TAP_MODE) { mTouchMode = TOUCH_DONE_MODE; } break; } case SWITCH_TO_LONGPRESS: { - if (mPreventLongPress) { - mTouchMode = TOUCH_DONE_MODE; - WebViewCore.TouchEventData ted - = new WebViewCore.TouchEventData(); + if (inFullScreenMode() || mDeferTouchProcess) { + TouchEventData ted = new TouchEventData(); ted.mAction = WebViewCore.ACTION_LONGPRESS; ted.mX = viewToContentX((int) mLastTouchX + mScrollX); ted.mY = viewToContentY((int) mLastTouchY + mScrollY); - ted.mEventTime = SystemClock.uptimeMillis(); - // metaState for long press is tricky. Should it be the state - // when the press started or when the press was released? Or - // some intermediary key state? For simplicity for now, we - // don't set it. + // metaState for long press is tricky. Should it be the + // state when the press started or when the press was + // released? Or some intermediary key state? For + // simplicity for now, we don't set it. ted.mMetaState = 0; + ted.mReprocess = mDeferTouchProcess; mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted); - } else if (mPreventDrag != PREVENT_DRAG_YES) { + } else if (mPreventDefault != PREVENT_DEFAULT_YES) { mTouchMode = TOUCH_DONE_MODE; - if (mFullScreenHolder == null) { - performLongClick(); - rebuildWebTextView(); - } + performLongClick(); + rebuildWebTextView(); } break; } case RELEASE_SINGLE_TAP: { - if (mPreventDrag == PREVENT_DRAG_MAYBE_YES) { - // if mPreventDrag is not confirmed, cancel it so that - // it won't block panning the page. - mPreventDrag = PREVENT_DRAG_CANCEL; - mPreventLongPress = false; - mPreventDoubleTap = false; - // remove the pending TOUCH_EVENT and send a cancel - mWebViewCore.removeMessages(EventHub.TOUCH_EVENT); - WebViewCore.TouchEventData ted = new WebViewCore.TouchEventData(); - ted.mAction = MotionEvent.ACTION_CANCEL; - mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted); - } - if (mPreventDrag != PREVENT_DRAG_YES) { - mTouchMode = TOUCH_DONE_MODE; - doShortPress(); - } + doShortPress(); break; } case SCROLL_BY_MSG_ID: @@ -6191,23 +6279,81 @@ public class WebView extends AbsoluteLayout break; case PREVENT_TOUCH_ID: - if (msg.arg1 == MotionEvent.ACTION_DOWN) { - // dont override if mPreventDrag has been set to no due - // to time out - if (mPreventDrag == PREVENT_DRAG_MAYBE_YES) { - mPreventDrag = (msg.arg2 & TOUCH_PREVENT_DRAG) - == TOUCH_PREVENT_DRAG ? PREVENT_DRAG_YES - : PREVENT_DRAG_NO; - if (mPreventDrag == PREVENT_DRAG_YES) { - mTouchMode = TOUCH_DONE_MODE; - } else { - mPreventLongPress = - (msg.arg2 & TOUCH_PREVENT_LONGPRESS) - == TOUCH_PREVENT_LONGPRESS; - mPreventDoubleTap = - (msg.arg2 & TOUCH_PREVENT_DOUBLETAP) - == TOUCH_PREVENT_DOUBLETAP; + if (inFullScreenMode()) { + break; + } + if (msg.obj == null) { + if (msg.arg1 == MotionEvent.ACTION_DOWN + && mPreventDefault == PREVENT_DEFAULT_MAYBE_YES) { + // if prevent default is called from WebCore, UI + // will not handle the rest of the touch events any + // more. + mPreventDefault = msg.arg2 == 1 ? PREVENT_DEFAULT_YES + : PREVENT_DEFAULT_NO_FROM_TOUCH_DOWN; + } else if (msg.arg1 == MotionEvent.ACTION_MOVE + && mPreventDefault == PREVENT_DEFAULT_NO_FROM_TOUCH_DOWN) { + // the return for the first ACTION_MOVE will decide + // whether UI will handle touch or not. Currently no + // support for alternating prevent default + mPreventDefault = msg.arg2 == 1 ? PREVENT_DEFAULT_YES + : PREVENT_DEFAULT_NO; + } + } else if (msg.arg2 == 0) { + // prevent default is not called in WebCore, so the + // message needs to be reprocessed in UI + TouchEventData ted = (TouchEventData) msg.obj; + switch (ted.mAction) { + case MotionEvent.ACTION_DOWN: + mLastDeferTouchX = contentToViewX(ted.mX) + - mScrollX; + mLastDeferTouchY = contentToViewY(ted.mY) + - mScrollY; + mDeferTouchMode = TOUCH_INIT_MODE; + break; + case MotionEvent.ACTION_MOVE: { + // no snapping in defer process + int x = contentToViewX(ted.mX) - mScrollX; + int y = contentToViewY(ted.mY) - mScrollY; + if (mDeferTouchMode != TOUCH_DRAG_MODE) { + mDeferTouchMode = TOUCH_DRAG_MODE; + mLastDeferTouchX = x; + mLastDeferTouchY = y; + startDrag(); + } + doDrag((int) (mLastDeferTouchX - x), + (int) (mLastDeferTouchY - y)); + mLastDeferTouchX = x; + mLastDeferTouchY = y; + break; } + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_CANCEL: + if (mDeferTouchMode == TOUCH_DRAG_MODE) { + // no fling in defer process + mScroller.springback(mScrollX, mScrollY, 0, + computeMaxScrollX(), 0, + computeMaxScrollY()); + invalidate(); + WebViewCore.resumePriority(); + } + mDeferTouchMode = TOUCH_DONE_MODE; + break; + case WebViewCore.ACTION_DOUBLETAP: + // doDoubleTap() needs mLastTouchX/Y as anchor + mLastTouchX = contentToViewX(ted.mX) - mScrollX; + mLastTouchY = contentToViewY(ted.mY) - mScrollY; + doDoubleTap(); + mDeferTouchMode = TOUCH_DONE_MODE; + break; + case WebViewCore.ACTION_LONGPRESS: + HitTestResult hitTest = getHitTestResult(); + if (hitTest != null && hitTest.mType + != HitTestResult.UNKNOWN_TYPE) { + performLongClick(); + rebuildWebTextView(); + } + mDeferTouchMode = TOUCH_DONE_MODE; + break; } } break; @@ -6251,7 +6397,7 @@ public class WebView extends AbsoluteLayout WebViewCore.PluginFullScreenData data = (WebViewCore.PluginFullScreenData) msg.obj; if (data.mNpp != 0 && data.mView != null) { - if (mFullScreenHolder != null) { + if (inFullScreenMode()) { Log.w(LOGTAG, "Should not have another full screen."); mFullScreenHolder.dismiss(); @@ -6269,7 +6415,7 @@ public class WebView extends AbsoluteLayout mFullScreenHolder.setCancelable(false); mFullScreenHolder.setCanceledOnTouchOutside(false); mFullScreenHolder.show(); - } else if (mFullScreenHolder == null) { + } else if (!inFullScreenMode()) { // this may happen if user dismisses the fullscreen and // then the WebCore re-position message finally reached // the UI thread. @@ -6323,7 +6469,7 @@ public class WebView extends AbsoluteLayout break; case HIDE_FULLSCREEN: - if (mFullScreenHolder != null) { + if (inFullScreenMode()) { mFullScreenHolder.dismiss(); mFullScreenHolder = null; } diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java index b339015..27c7208 100644 --- a/core/java/android/webkit/WebViewCore.java +++ b/core/java/android/webkit/WebViewCore.java @@ -515,7 +515,8 @@ final class WebViewCore { private native void nativeTouchUp(int touchGeneration, int framePtr, int nodePtr, int x, int y); - private native int nativeHandleTouchEvent(int action, int x, int y, long time, int metaState); + private native boolean nativeHandleTouchEvent(int action, int x, int y, + int metaState); private native void nativeUpdateFrameCache(); @@ -705,8 +706,8 @@ final class WebViewCore { int mAction; int mX; int mY; - long mEventTime; int mMetaState; + boolean mReprocess; } static class GeolocationPermissionsData { @@ -1160,9 +1161,11 @@ final class WebViewCore { TouchEventData ted = (TouchEventData) msg.obj; Message.obtain( mWebView.mPrivateHandler, - WebView.PREVENT_TOUCH_ID, ted.mAction, + WebView.PREVENT_TOUCH_ID, + ted.mAction, nativeHandleTouchEvent(ted.mAction, ted.mX, - ted.mY, ted.mEventTime, ted.mMetaState)).sendToTarget(); + ted.mY, ted.mMetaState) ? 1 : 0, + ted.mReprocess ? ted : null).sendToTarget(); break; } diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java index e87455c..a1c3fa2 100644 --- a/core/java/android/widget/AbsListView.java +++ b/core/java/android/widget/AbsListView.java @@ -1641,7 +1641,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te mResurrectToPosition = mSelectedPosition; } } else { - if (mFiltered) { + if (mFiltered && !mPopupHidden) { // Show the type filter only if a filter is in effect showPopup(); } @@ -2773,7 +2773,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te final int lastPos = firstPos + getChildCount() - 1; int viewTravelCount = 0; - if (position < firstPos) { + if (position <= firstPos) { final int boundPosFromLast = lastPos - boundPosition; if (boundPosFromLast < 1) { // Moving would shift our bound position off the screen. Abort. @@ -2789,7 +2789,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te viewTravelCount = posTravel; mMode = MOVE_UP_POS; } - } else if (position > lastPos) { + } else if (position >= lastPos) { final int boundPosFromFirst = boundPosition - firstPos; if (boundPosFromFirst < 1) { // Moving would shift our bound position off the screen. Abort. @@ -3510,7 +3510,6 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te if (mPopup != null) { mPopup.dismiss(); } - mPopupHidden = false; } /** diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java index 1a9c0df5..d4552e3 100644 --- a/core/java/android/widget/ListView.java +++ b/core/java/android/widget/ListView.java @@ -3480,10 +3480,41 @@ public class ListView extends AbsListView { * @return A new array which contains the id of each checked item in the * list. * - * @deprecated Use {@link #getCheckedItemIds()} instead. + * @deprecated Use {@link #getCheckedItemIds()} instead. */ public long[] getCheckItemIds() { - return getCheckedItemIds(); + // Use new behavior that correctly handles stable ID mapping. + if (mAdapter != null && mAdapter.hasStableIds()) { + return getCheckedItemIds(); + } + + // Old behavior was buggy, but would sort of work for adapters without stable IDs. + // Fall back to it to support legacy apps. + if (mChoiceMode != CHOICE_MODE_NONE && mCheckStates != null && mAdapter != null) { + final SparseBooleanArray states = mCheckStates; + final int count = states.size(); + final long[] ids = new long[count]; + final ListAdapter adapter = mAdapter; + + int checkedCount = 0; + for (int i = 0; i < count; i++) { + if (states.valueAt(i)) { + ids[checkedCount++] = adapter.getItemId(states.keyAt(i)); + } + } + + // Trim array if needed. mCheckStates may contain false values + // resulting in checkedCount being smaller than count. + if (checkedCount == count) { + return ids; + } else { + final long[] result = new long[checkedCount]; + System.arraycopy(ids, 0, result, 0, checkedCount); + + return result; + } + } + return new long[0]; } /** diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 7b21be5..64c9c99 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -6989,6 +6989,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener @Override public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { + if (!isShown()) { + return false; + } + final boolean isPassword = isPasswordInputType(mInputType); if (!isPassword) { diff --git a/core/java/android/widget/VideoView.java b/core/java/android/widget/VideoView.java index fb8b5aa..ded0559 100644 --- a/core/java/android/widget/VideoView.java +++ b/core/java/android/widget/VideoView.java @@ -291,7 +291,7 @@ public class VideoView extends SurfaceView implements MediaPlayerControl { mCanSeekForward = !data.has(Metadata.SEEK_FORWARD_AVAILABLE) || data.getBoolean(Metadata.SEEK_FORWARD_AVAILABLE); } else { - mCanPause = mCanSeekForward = mCanSeekForward = true; + mCanPause = mCanSeekBack = mCanSeekForward = true; } if (mOnPreparedListener != null) { diff --git a/core/java/com/android/internal/app/IMediaContainerService.aidl b/core/java/com/android/internal/app/IMediaContainerService.aidl index c0e9587..fd1fd58 100755 --- a/core/java/com/android/internal/app/IMediaContainerService.aidl +++ b/core/java/com/android/internal/app/IMediaContainerService.aidl @@ -18,6 +18,7 @@ package com.android.internal.app; import android.net.Uri; import android.os.ParcelFileDescriptor; +import android.content.pm.PackageInfoLite; interface IMediaContainerService { String copyResourceToContainer(in Uri packageURI, @@ -25,5 +26,5 @@ interface IMediaContainerService { String key, String resFileName); boolean copyResource(in Uri packageURI, in ParcelFileDescriptor outStream); - int getRecommendedInstallLocation(in Uri fileUri); + PackageInfoLite getMinimalPackageInfo(in Uri fileUri); }
\ No newline at end of file diff --git a/core/java/com/android/internal/app/ShutdownThread.java b/core/java/com/android/internal/app/ShutdownThread.java index 37898a1..51cd0f8 100644 --- a/core/java/com/android/internal/app/ShutdownThread.java +++ b/core/java/com/android/internal/app/ShutdownThread.java @@ -28,12 +28,13 @@ import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.os.Handler; +import android.os.Power; import android.os.PowerManager; import android.os.RemoteException; -import android.os.Power; import android.os.ServiceManager; import android.os.SystemClock; import android.os.storage.IMountService; +import android.os.storage.IMountShutdownObserver; import com.android.internal.telephony.ITelephony; import android.util.Log; @@ -46,16 +47,20 @@ public final class ShutdownThread extends Thread { private static final int PHONE_STATE_POLL_SLEEP_MSEC = 500; // maximum time we wait for the shutdown broadcast before going on. private static final int MAX_BROADCAST_TIME = 10*1000; + private static final int MAX_SHUTDOWN_WAIT_TIME = 20*1000; // state tracking private static Object sIsStartedGuard = new Object(); private static boolean sIsStarted = false; + private static boolean mReboot; + private static String mRebootReason; + // static instance of this thread private static final ShutdownThread sInstance = new ShutdownThread(); - private final Object mBroadcastDoneSync = new Object(); - private boolean mBroadcastDone; + private final Object mActionDoneSync = new Object(); + private boolean mActionDone; private Context mContext; private PowerManager mPowerManager; private PowerManager.WakeLock mWakeLock; @@ -64,12 +69,13 @@ public final class ShutdownThread extends Thread { private ShutdownThread() { } - /** + /** * Request a clean shutdown, waiting for subsystems to clean up their * state etc. Must be called from a Looper thread in which its UI * is shown. - * + * * @param context Context used to display the shutdown progress dialog. + * @param confirm true if user confirmation is needed before shutting down. */ public static void shutdown(final Context context, boolean confirm) { // ensure that only one thread is trying to power down. @@ -106,6 +112,21 @@ public final class ShutdownThread extends Thread { } } + /** + * Request a clean shutdown, waiting for subsystems to clean up their + * state etc. Must be called from a Looper thread in which its UI + * is shown. + * + * @param context Context used to display the shutdown progress dialog. + * @param reason code to pass to the kernel (e.g. "recovery"), or null. + * @param confirm true if user confirmation is needed before shutting down. + */ + public static void reboot(final Context context, String reason, boolean confirm) { + mReboot = true; + mRebootReason = reason; + shutdown(context, confirm); + } + private static void beginShutdownSequence(Context context) { synchronized (sIsStartedGuard) { sIsStarted = true; @@ -145,13 +166,13 @@ public final class ShutdownThread extends Thread { sInstance.start(); } - void broadcastDone() { - synchronized (mBroadcastDoneSync) { - mBroadcastDone = true; - mBroadcastDoneSync.notifyAll(); + void actionDone() { + synchronized (mActionDoneSync) { + mActionDone = true; + mActionDoneSync.notifyAll(); } } - + /** * Makes sure we handle the shutdown gracefully. * Shuts off power regardless of radio and bluetooth state if the alloted time has passed. @@ -163,27 +184,27 @@ public final class ShutdownThread extends Thread { BroadcastReceiver br = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { // We don't allow apps to cancel this, so ignore the result. - broadcastDone(); + actionDone(); } }; Log.i(TAG, "Sending shutdown broadcast..."); // First send the high-level shut down broadcast. - mBroadcastDone = false; + mActionDone = false; mContext.sendOrderedBroadcast(new Intent(Intent.ACTION_SHUTDOWN), null, br, mHandler, 0, null, null); final long endTime = System.currentTimeMillis() + MAX_BROADCAST_TIME; - synchronized (mBroadcastDoneSync) { - while (!mBroadcastDone) { + synchronized (mActionDoneSync) { + while (!mActionDone) { long delay = endTime - System.currentTimeMillis(); if (delay <= 0) { Log.w(TAG, "Shutdown broadcast timed out"); break; } try { - mBroadcastDoneSync.wait(delay); + mActionDoneSync.wait(delay); } catch (InterruptedException e) { } } @@ -262,17 +283,50 @@ public final class ShutdownThread extends Thread { } // Shutdown MountService to ensure media is in a safe state - try { - if (mount != null) { - mount.shutdown(); - } else { - Log.w(TAG, "MountService unavailable for shutdown"); + IMountShutdownObserver observer = new IMountShutdownObserver.Stub() { + public void onShutDownComplete(int statusCode) throws RemoteException { + Log.w(TAG, "Result code " + statusCode + " from MountService.shutdown"); + actionDone(); + } + }; + + Log.i(TAG, "Shutting down MountService"); + // Set initial variables and time out time. + mActionDone = false; + final long endShutTime = System.currentTimeMillis() + MAX_SHUTDOWN_WAIT_TIME; + synchronized (mActionDoneSync) { + try { + if (mount != null) { + mount.shutdown(observer); + } else { + Log.w(TAG, "MountService unavailable for shutdown"); + } + } catch (Exception e) { + Log.e(TAG, "Exception during MountService shutdown", e); + } + while (!mActionDone) { + long delay = endShutTime - System.currentTimeMillis(); + if (delay <= 0) { + Log.w(TAG, "Shutdown wait timed out"); + break; + } + try { + mActionDoneSync.wait(delay); + } catch (InterruptedException e) { + } + } + } + + if (mReboot) { + Log.i(TAG, "Rebooting, reason: " + mRebootReason); + try { + Power.reboot(mRebootReason); + } catch (Exception e) { + Log.e(TAG, "Reboot failed, will attempt shutdown instead", e); } - } catch (Exception e) { - Log.e(TAG, "Exception during MountService shutdown", e); } - //shutdown power + // Shutdown power Log.i(TAG, "Performing low-level shutdown..."); Power.shutdown(); } diff --git a/core/java/com/android/internal/content/PackageHelper.java b/core/java/com/android/internal/content/PackageHelper.java index 04a10b9..de6a175 100644 --- a/core/java/com/android/internal/content/PackageHelper.java +++ b/core/java/com/android/internal/content/PackageHelper.java @@ -36,6 +36,8 @@ public class PackageHelper { public static final int RECOMMEND_INSTALL_EXTERNAL = 2; public static final int RECOMMEND_FAILED_INSUFFICIENT_STORAGE = -1; public static final int RECOMMEND_FAILED_INVALID_APK = -2; + public static final int RECOMMEND_FAILED_INVALID_LOCATION = -3; + public static final int RECOMMEND_FAILED_ALREADY_EXISTS = -4; private static final boolean localLOGV = true; private static final String TAG = "PackageHelper"; diff --git a/core/java/com/android/internal/content/SelectionBuilder.java b/core/java/com/android/internal/content/SelectionBuilder.java new file mode 100644 index 0000000..0e29411 --- /dev/null +++ b/core/java/com/android/internal/content/SelectionBuilder.java @@ -0,0 +1,121 @@ +/* + * 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 com.android.internal.content; + +import android.content.ContentValues; +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; +import android.text.TextUtils; + +import java.util.ArrayList; + +/** + * Helper for building selection clauses for {@link SQLiteDatabase}. Each + * appended clause is combined using {@code AND}. This class is <em>not</em> + * thread safe. + * + * @hide + */ +public class SelectionBuilder { + private StringBuilder mSelection = new StringBuilder(); + private ArrayList<String> mSelectionArgs = new ArrayList<String>(); + + /** + * Reset any internal state, allowing this builder to be recycled. + */ + public SelectionBuilder reset() { + mSelection.setLength(0); + mSelectionArgs.clear(); + return this; + } + + /** + * Append the given selection clause to the internal state. Each clause is + * surrounded with parenthesis and combined using {@code AND}. + */ + public SelectionBuilder append(String selection, String... selectionArgs) { + if (TextUtils.isEmpty(selection)) { + if (selectionArgs != null && selectionArgs.length > 0) { + throw new IllegalArgumentException( + "Valid selection required when including arguments"); + } + + // Shortcut when clause is empty + return this; + } + + if (mSelection.length() > 0) { + mSelection.append(" AND "); + } + + mSelection.append("(").append(selection).append(")"); + for (String arg : selectionArgs) { + mSelectionArgs.add(arg); + } + + return this; + } + + /** + * Return selection string for current internal state. + * + * @see #getSelectionArgs() + */ + public String getSelection() { + return mSelection.toString(); + } + + /** + * Return selection arguments for current internal state. + * + * @see #getSelection() + */ + public String[] getSelectionArgs() { + return mSelectionArgs.toArray(new String[mSelectionArgs.size()]); + } + + /** + * Execute query using the current internal state as {@code WHERE} clause. + * Missing arguments as treated as {@code null}. + */ + public Cursor query(SQLiteDatabase db, String table, String[] columns, String orderBy) { + return query(db, table, columns, null, null, orderBy, null); + } + + /** + * Execute query using the current internal state as {@code WHERE} clause. + */ + public Cursor query(SQLiteDatabase db, String table, String[] columns, String groupBy, + String having, String orderBy, String limit) { + return db.query(table, columns, getSelection(), getSelectionArgs(), groupBy, having, + orderBy, limit); + } + + /** + * Execute update using the current internal state as {@code WHERE} clause. + */ + public int update(SQLiteDatabase db, String table, ContentValues values) { + return db.update(table, values, getSelection(), getSelectionArgs()); + } + + /** + * Execute delete using the current internal state as {@code WHERE} clause. + */ + public int delete(SQLiteDatabase db, String table) { + return db.delete(table, getSelection(), getSelectionArgs()); + } +} diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp index 9965fe5..b6f3997 100644 --- a/core/jni/android/graphics/BitmapFactory.cpp +++ b/core/jni/android/graphics/BitmapFactory.cpp @@ -331,12 +331,14 @@ static SkPixelRef* installPixelRef(SkBitmap* bitmap, SkStream* stream, // i.e. dynamically allocated, since its lifetime may exceed the current stack // frame. static jobject doDecode(JNIEnv* env, SkStream* stream, jobject padding, - jobject options, bool allowPurgeable) { + jobject options, bool allowPurgeable, + bool forcePurgeable = false) { int sampleSize = 1; SkImageDecoder::Mode mode = SkImageDecoder::kDecodePixels_Mode; SkBitmap::Config prefConfig = SkBitmap::kNo_Config; bool doDither = true; - bool isPurgeable = allowPurgeable && optionsPurgeable(env, options); + bool isPurgeable = forcePurgeable || + (allowPurgeable && optionsPurgeable(env, options)); bool reportSizeToVM = optionsReportSizeToVM(env, options); if (NULL != options) { @@ -568,8 +570,10 @@ static jobject nativeDecodeAsset(JNIEnv* env, jobject clazz, jobject options) { // BitmapFactory$Options SkStream* stream; Asset* asset = reinterpret_cast<Asset*>(native_asset); + // assets can always be rebuilt, so force this + bool forcePurgeable = true; - if (optionsPurgeable(env, options)) { + if (forcePurgeable || optionsPurgeable(env, options)) { // if we could "ref/reopen" the asset, we may not need to copy it here // and we could assume optionsShareable, since assets are always RO stream = copyAssetToStream(asset); @@ -582,7 +586,7 @@ static jobject nativeDecodeAsset(JNIEnv* env, jobject clazz, stream = new AssetStreamAdaptor(asset); } SkAutoUnref aur(stream); - return doDecode(env, stream, padding, options, true); + return doDecode(env, stream, padding, options, true, forcePurgeable); } static jobject nativeDecodeByteArray(JNIEnv* env, jobject, jbyteArray byteArray, diff --git a/core/jni/android/graphics/Graphics.cpp b/core/jni/android/graphics/Graphics.cpp index 01aad93..5659ba2 100644 --- a/core/jni/android/graphics/Graphics.cpp +++ b/core/jni/android/graphics/Graphics.cpp @@ -1,3 +1,5 @@ +#define LOG_TAG "GraphicsJNI" + #include "jni.h" #include "GraphicsJNI.h" #include "NIOBuffer.h" diff --git a/core/jni/android/graphics/YuvToJpegEncoder.cpp b/core/jni/android/graphics/YuvToJpegEncoder.cpp index 819cce8..0a0c5b3 100644 --- a/core/jni/android/graphics/YuvToJpegEncoder.cpp +++ b/core/jni/android/graphics/YuvToJpegEncoder.cpp @@ -49,19 +49,16 @@ bool YuvToJpegEncoder::encode(SkWStream* stream, void* inYuv, int width, void YuvToJpegEncoder::setJpegCompressStruct(jpeg_compress_struct* cinfo, int width, int height, int quality) { - jpeg_set_quality(cinfo, quality, TRUE); - cinfo->image_width = width; cinfo->image_height = height; - cinfo->input_components = 3; cinfo->in_color_space = JCS_YCbCr; jpeg_set_defaults(cinfo); + + jpeg_set_quality(cinfo, quality, TRUE); jpeg_set_colorspace(cinfo, JCS_YCbCr); cinfo->raw_data_in = TRUE; - cinfo->dct_method = JDCT_IFAST; - configSamplingFactors(cinfo); } diff --git a/core/jni/android_server_BluetoothService.cpp b/core/jni/android_server_BluetoothService.cpp index ea64305..4420aca 100644 --- a/core/jni/android_server_BluetoothService.cpp +++ b/core/jni/android_server_BluetoothService.cpp @@ -861,6 +861,26 @@ static jboolean removeServiceRecordNative(JNIEnv *env, jobject object, jint hand return JNI_FALSE; } +static jboolean setLinkTimeoutNative(JNIEnv *env, jobject object, jstring object_path, + jint num_slots) { + LOGV(__FUNCTION__); +#ifdef HAVE_BLUETOOTH + native_data_t *nat = get_native_data(env, object); + if (nat) { + const char *c_object_path = env->GetStringUTFChars(object_path, NULL); + DBusMessage *reply = dbus_func_args(env, nat->conn, + get_adapter_path(env, object), + DBUS_ADAPTER_IFACE, "SetLinkTimeout", + DBUS_TYPE_OBJECT_PATH, &c_object_path, + DBUS_TYPE_UINT32, &num_slots, + DBUS_TYPE_INVALID); + env->ReleaseStringUTFChars(object_path, c_object_path); + return reply ? JNI_TRUE : JNI_FALSE; + } +#endif + return JNI_FALSE; +} + static JNINativeMethod sMethods[] = { /* name, signature, funcPtr */ {"classInitNative", "()V", (void*)classInitNative}, @@ -905,6 +925,7 @@ static JNINativeMethod sMethods[] = { {"discoverServicesNative", "(Ljava/lang/String;Ljava/lang/String;)Z", (void *)discoverServicesNative}, {"addRfcommServiceRecordNative", "(Ljava/lang/String;JJS)I", (void *)addRfcommServiceRecordNative}, {"removeServiceRecordNative", "(I)Z", (void *)removeServiceRecordNative}, + {"setLinkTimeoutNative", "(Ljava/lang/String;I)Z", (void *)setLinkTimeoutNative}, }; int register_android_server_BluetoothService(JNIEnv *env) { diff --git a/core/res/res/drawable-hdpi/stat_notify_car_mode.png b/core/res/res/drawable-hdpi/stat_notify_car_mode.png Binary files differindex 6c51b32..03499a4 100644 --- a/core/res/res/drawable-hdpi/stat_notify_car_mode.png +++ b/core/res/res/drawable-hdpi/stat_notify_car_mode.png diff --git a/core/res/res/drawable-hdpi/stat_sys_tether_bluetooth.png b/core/res/res/drawable-hdpi/stat_sys_tether_bluetooth.png Binary files differnew file mode 100644 index 0000000..e43fbae --- /dev/null +++ b/core/res/res/drawable-hdpi/stat_sys_tether_bluetooth.png diff --git a/core/res/res/drawable-hdpi/stat_sys_tether_general.png b/core/res/res/drawable-hdpi/stat_sys_tether_general.png Binary files differnew file mode 100644 index 0000000..e43fbae --- /dev/null +++ b/core/res/res/drawable-hdpi/stat_sys_tether_general.png diff --git a/core/res/res/drawable-hdpi/stat_sys_tether_usb.png b/core/res/res/drawable-hdpi/stat_sys_tether_usb.png Binary files differindex 4c14c07..c6c533d 100755..100644 --- a/core/res/res/drawable-hdpi/stat_sys_tether_usb.png +++ b/core/res/res/drawable-hdpi/stat_sys_tether_usb.png diff --git a/core/res/res/drawable-hdpi/stat_sys_tether_wifi.png b/core/res/res/drawable-hdpi/stat_sys_tether_wifi.png Binary files differnew file mode 100644 index 0000000..9fcadef --- /dev/null +++ b/core/res/res/drawable-hdpi/stat_sys_tether_wifi.png diff --git a/core/res/res/drawable-mdpi/stat_notify_car_mode.png b/core/res/res/drawable-mdpi/stat_notify_car_mode.png Binary files differindex c664244..0272e6b 100644 --- a/core/res/res/drawable-mdpi/stat_notify_car_mode.png +++ b/core/res/res/drawable-mdpi/stat_notify_car_mode.png diff --git a/core/res/res/drawable-mdpi/stat_sys_tether_bluetooth.png b/core/res/res/drawable-mdpi/stat_sys_tether_bluetooth.png Binary files differnew file mode 100644 index 0000000..efb64ad --- /dev/null +++ b/core/res/res/drawable-mdpi/stat_sys_tether_bluetooth.png diff --git a/core/res/res/drawable-mdpi/stat_sys_tether_general.png b/core/res/res/drawable-mdpi/stat_sys_tether_general.png Binary files differnew file mode 100644 index 0000000..efb64ad --- /dev/null +++ b/core/res/res/drawable-mdpi/stat_sys_tether_general.png diff --git a/core/res/res/drawable-mdpi/stat_sys_tether_usb.png b/core/res/res/drawable-mdpi/stat_sys_tether_usb.png Binary files differindex 2d0da4c..73f1a81 100644 --- a/core/res/res/drawable-mdpi/stat_sys_tether_usb.png +++ b/core/res/res/drawable-mdpi/stat_sys_tether_usb.png diff --git a/core/res/res/drawable-mdpi/stat_sys_tether_wifi.png b/core/res/res/drawable-mdpi/stat_sys_tether_wifi.png Binary files differnew file mode 100644 index 0000000..d448491 --- /dev/null +++ b/core/res/res/drawable-mdpi/stat_sys_tether_wifi.png diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index e68f214..3fb9607 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -2251,4 +2251,8 @@ <string name="car_mode_disable_notification_title">Car mode enabled</string> <string name="car_mode_disable_notification_message">Select to exit car mode.</string> + <!-- Strings for tethered notification --> + <!-- Shown when the device is tethered --> + <string name="tethered_notification_title">Tethering active</string> + <string name="tethered_notification_message">Touch to configure</string> </resources> diff --git a/include/media/stagefright/StagefrightMediaScanner.h b/include/media/stagefright/StagefrightMediaScanner.h index af125dc..4437eee 100644 --- a/include/media/stagefright/StagefrightMediaScanner.h +++ b/include/media/stagefright/StagefrightMediaScanner.h @@ -22,7 +22,7 @@ namespace android { -struct StagefrightMetadataRetriever; +struct MediaMetadataRetriever; struct StagefrightMediaScanner : public MediaScanner { StagefrightMediaScanner(); @@ -35,7 +35,7 @@ struct StagefrightMediaScanner : public MediaScanner { virtual char *extractAlbumArt(int fd); private: - sp<StagefrightMetadataRetriever> mRetriever; + sp<MediaMetadataRetriever> mRetriever; StagefrightMediaScanner(const StagefrightMediaScanner &); StagefrightMediaScanner &operator=(const StagefrightMediaScanner &); diff --git a/include/utils/ResourceTypes.h b/include/utils/ResourceTypes.h index 0e796dc..b701ce7 100644 --- a/include/utils/ResourceTypes.h +++ b/include/utils/ResourceTypes.h @@ -631,6 +631,8 @@ public: void restart(); + const ResStringPool& getStrings() const; + event_code_t getEventType() const; // Note, unlike XmlPullParser, the first call to next() will return // START_TAG of the first element. @@ -716,8 +718,6 @@ public: void uninit(); - const ResStringPool& getStrings() const; - private: friend class ResXMLParser; diff --git a/libs/audioflinger/Android.mk b/libs/audioflinger/Android.mk index b68bfc1..870c0b8 100644 --- a/libs/audioflinger/Android.mk +++ b/libs/audioflinger/Android.mk @@ -24,7 +24,7 @@ endif LOCAL_SHARED_LIBRARIES := \ libcutils \ libutils \ - libbinder \ + libbinder \ libmedia \ libhardware_legacy @@ -85,7 +85,7 @@ LOCAL_SRC_FILES:= \ LOCAL_SHARED_LIBRARIES := \ libcutils \ libutils \ - libbinder \ + libbinder \ libmedia \ libhardware_legacy @@ -114,9 +114,17 @@ ifeq ($(AUDIO_POLICY_TEST),true) endif ifeq ($(TARGET_SIMULATOR),true) - ifeq ($(HOST_OS),linux) - LOCAL_LDLIBS += -lrt -lpthread - endif + ifeq ($(HOST_OS),linux) + LOCAL_LDLIBS += -lrt -lpthread + endif +endif + +ifeq ($(BOARD_USE_LVMX),true) + LOCAL_CFLAGS += -DLVMX + LOCAL_C_INCLUDES += vendor/nxp + LOCAL_STATIC_LIBRARIES += liblifevibes + LOCAL_SHARED_LIBRARIES += liblvmxservice +# LOCAL_SHARED_LIBRARIES += liblvmxipc endif include $(BUILD_SHARED_LIBRARY) diff --git a/libs/audioflinger/AudioFlinger.cpp b/libs/audioflinger/AudioFlinger.cpp index 8089389..7902212 100644 --- a/libs/audioflinger/AudioFlinger.cpp +++ b/libs/audioflinger/AudioFlinger.cpp @@ -47,6 +47,10 @@ #include "A2dpAudioInterface.h" #endif +#ifdef LVMX +#include "lifevibes.h" +#endif + // ---------------------------------------------------------------------------- // the sim build doesn't have gettid @@ -132,6 +136,9 @@ AudioFlinger::AudioFlinger() } else { LOGE("Couldn't even initialize the stubbed audio hardware!"); } +#ifdef LVMX + LifeVibes::init(); +#endif } AudioFlinger::~AudioFlinger() @@ -411,6 +418,11 @@ status_t AudioFlinger::setMode(int mode) AutoMutex lock(mHardwareLock); mHardwareStatus = AUDIO_HW_SET_MODE; status_t ret = mAudioHardware->setMode(mode); +#ifdef LVMX + if (NO_ERROR == ret) { + LifeVibes::setMode(mode); + } +#endif mHardwareStatus = AUDIO_HW_IDLE; return ret; } @@ -566,11 +578,37 @@ status_t AudioFlinger::setParameters(int ioHandle, const String8& keyValuePairs) return PERMISSION_DENIED; } +#ifdef LVMX + AudioParameter param = AudioParameter(keyValuePairs); + LifeVibes::setParameters(ioHandle,keyValuePairs); + String8 key = String8(AudioParameter::keyRouting); + int device; + if (NO_ERROR != param.getInt(key, device)) { + device = -1; + } + + key = String8(LifevibesTag); + String8 value; + int musicEnabled = -1; + if (NO_ERROR == param.get(key, value)) { + if (value == LifevibesEnable) { + musicEnabled = 1; + } else if (value == LifevibesDisable) { + musicEnabled = 0; + } + } +#endif + // ioHandle == 0 means the parameters are global to the audio hardware interface if (ioHandle == 0) { AutoMutex lock(mHardwareLock); mHardwareStatus = AUDIO_SET_PARAMETER; result = mAudioHardware->setParameters(keyValuePairs); +#ifdef LVMX + if ((NO_ERROR == result) && (musicEnabled != -1)) { + LifeVibes::enableMusic((bool) musicEnabled); + } +#endif mHardwareStatus = AUDIO_HW_IDLE; return result; } @@ -586,7 +624,13 @@ status_t AudioFlinger::setParameters(int ioHandle, const String8& keyValuePairs) } } if (thread != NULL) { - return thread->setParameters(keyValuePairs); + result = thread->setParameters(keyValuePairs); +#ifdef LVMX + if ((NO_ERROR == result) && (device != -1)) { + LifeVibes::setDevice(LifeVibes::threadIdToAudioOutputType(thread->id()), device); + } +#endif + return result; } return BAD_VALUE; } @@ -1058,12 +1102,24 @@ uint32_t AudioFlinger::PlaybackThread::latency() const status_t AudioFlinger::PlaybackThread::setMasterVolume(float value) { +#ifdef LVMX + int audioOutputType = LifeVibes::getMixerType(mId, mType); + if (LifeVibes::audioOutputTypeIsLifeVibes(audioOutputType)) { + LifeVibes::setMasterVolume(audioOutputType, value); + } +#endif mMasterVolume = value; return NO_ERROR; } status_t AudioFlinger::PlaybackThread::setMasterMute(bool muted) { +#ifdef LVMX + int audioOutputType = LifeVibes::getMixerType(mId, mType); + if (LifeVibes::audioOutputTypeIsLifeVibes(audioOutputType)) { + LifeVibes::setMasterMute(audioOutputType, muted); + } +#endif mMasterMute = muted; return NO_ERROR; } @@ -1080,12 +1136,24 @@ bool AudioFlinger::PlaybackThread::masterMute() const status_t AudioFlinger::PlaybackThread::setStreamVolume(int stream, float value) { +#ifdef LVMX + int audioOutputType = LifeVibes::getMixerType(mId, mType); + if (LifeVibes::audioOutputTypeIsLifeVibes(audioOutputType)) { + LifeVibes::setStreamVolume(audioOutputType, stream, value); + } +#endif mStreamTypes[stream].volume = value; return NO_ERROR; } status_t AudioFlinger::PlaybackThread::setStreamMute(int stream, bool muted) { +#ifdef LVMX + int audioOutputType = LifeVibes::getMixerType(mId, mType); + if (LifeVibes::audioOutputTypeIsLifeVibes(audioOutputType)) { + LifeVibes::setStreamMute(audioOutputType, stream, muted); + } +#endif mStreamTypes[stream].mute = muted; return NO_ERROR; } @@ -1333,6 +1401,12 @@ bool AudioFlinger::MixerThread::threadLoop() mLastWriteTime = systemTime(); mInWrite = true; mBytesWritten += mixBufferSize; +#ifdef LVMX + int audioOutputType = LifeVibes::getMixerType(mId, mType); + if (LifeVibes::audioOutputTypeIsLifeVibes(audioOutputType)) { + LifeVibes::process(audioOutputType, curBuf, mixBufferSize); + } +#endif int bytesWritten = (int)mOutput->write(curBuf, mixBufferSize); if (bytesWritten < 0) mBytesWritten -= mixBufferSize; mNumWrites++; @@ -1376,6 +1450,29 @@ uint32_t AudioFlinger::MixerThread::prepareTracks_l(const SortedVector< wp<Track uint32_t mixerStatus = MIXER_IDLE; // find out which tracks need to be processed size_t count = activeTracks.size(); + + float masterVolume = mMasterVolume; + bool masterMute = mMasterMute; + +#ifdef LVMX + bool tracksConnectedChanged = false; + bool stateChanged = false; + + int audioOutputType = LifeVibes::getMixerType(mId, mType); + if (LifeVibes::audioOutputTypeIsLifeVibes(audioOutputType)) + { + int activeTypes = 0; + for (size_t i=0 ; i<count ; i++) { + sp<Track> t = activeTracks[i].promote(); + if (t == 0) continue; + Track* const track = t.get(); + int iTracktype=track->type(); + activeTypes |= 1<<track->type(); + } + LifeVibes::computeVolumes(audioOutputType, activeTypes, tracksConnectedChanged, stateChanged, masterVolume, masterMute); + } +#endif + for (size_t i=0 ; i<count ; i++) { sp<Track> t = activeTracks[i].promote(); if (t == 0) continue; @@ -1393,15 +1490,27 @@ uint32_t AudioFlinger::MixerThread::prepareTracks_l(const SortedVector< wp<Track // compute volume for this track int16_t left, right; - if (track->isMuted() || mMasterMute || track->isPausing() || + if (track->isMuted() || masterMute || track->isPausing() || mStreamTypes[track->type()].mute) { left = right = 0; if (track->isPausing()) { track->setPaused(); } } else { + // read original volumes with volume control float typeVolume = mStreamTypes[track->type()].volume; - float v = mMasterVolume * typeVolume; +#ifdef LVMX + bool streamMute=false; + // read the volume from the LivesVibes audio engine. + if (LifeVibes::audioOutputTypeIsLifeVibes(audioOutputType)) + { + LifeVibes::getStreamVolumes(audioOutputType, track->type(), &typeVolume, &streamMute); + if (streamMute) { + typeVolume = 0; + } + } +#endif + float v = masterVolume * typeVolume; float v_clamped = v * cblk->volume[0]; if (v_clamped > MAX_GAIN) v_clamped = MAX_GAIN; left = int16_t(v_clamped); @@ -1427,7 +1536,13 @@ uint32_t AudioFlinger::MixerThread::prepareTracks_l(const SortedVector< wp<Track // do not apply ramp param = AudioMixer::RAMP_VOLUME; } - +#ifdef LVMX + if ( tracksConnectedChanged || stateChanged ) + { + // only do the ramp when the volume is changed by the user / application + param = AudioMixer::VOLUME; + } +#endif mAudioMixer->setParameter(param, AudioMixer::VOLUME0, left); mAudioMixer->setParameter(param, AudioMixer::VOLUME1, right); mAudioMixer->setParameter( @@ -3652,6 +3767,18 @@ int AudioFlinger::openOutput(uint32_t *pDevices, } else { thread = new MixerThread(this, output, ++mNextThreadId); LOGV("openOutput() created mixer output: ID %d thread %p", mNextThreadId, thread); + +#ifdef LVMX + unsigned bitsPerSample = + (format == AudioSystem::PCM_16_BIT) ? 16 : + ((format == AudioSystem::PCM_8_BIT) ? 8 : 0); + unsigned channelCount = (channels == AudioSystem::CHANNEL_OUT_STEREO) ? 2 : 1; + int audioOutputType = LifeVibes::threadIdToAudioOutputType(thread->id()); + + LifeVibes::init_aot(audioOutputType, samplingRate, bitsPerSample, channelCount); + LifeVibes::setDevice(audioOutputType, *pDevices); +#endif + } mPlaybackThreads.add(mNextThreadId, thread); diff --git a/libs/audioflinger/AudioHardwareStub.cpp b/libs/audioflinger/AudioHardwareStub.cpp index ae215d1..d481150 100644 --- a/libs/audioflinger/AudioHardwareStub.cpp +++ b/libs/audioflinger/AudioHardwareStub.cpp @@ -166,7 +166,7 @@ status_t AudioStreamOutStub::getRenderPosition(uint32_t *dspFrames) // ---------------------------------------------------------------------------- status_t AudioStreamInStub::set(int *pFormat, uint32_t *pChannels, uint32_t *pRate, - AudioSystem::audio_in_acoustics acoustics) + AudioSystem::audio_in_acoustics acoustics) { return NO_ERROR; } diff --git a/libs/audioflinger/AudioPolicyManagerBase.cpp b/libs/audioflinger/AudioPolicyManagerBase.cpp index cfcc3ea..2b0a6c8 100644 --- a/libs/audioflinger/AudioPolicyManagerBase.cpp +++ b/libs/audioflinger/AudioPolicyManagerBase.cpp @@ -344,6 +344,7 @@ void AudioPolicyManagerBase::setForceUse(AudioSystem::force_use usage, AudioSyst { LOGV("setForceUse() usage %d, config %d, mPhoneState %d", usage, config, mPhoneState); + bool forceVolumeReeval = false; switch(usage) { case AudioSystem::FOR_COMMUNICATION: if (config != AudioSystem::FORCE_SPEAKER && config != AudioSystem::FORCE_BT_SCO && @@ -374,6 +375,7 @@ void AudioPolicyManagerBase::setForceUse(AudioSystem::force_use usage, AudioSyst config != AudioSystem::FORCE_BT_DESK_DOCK && config != AudioSystem::FORCE_WIRED_ACCESSORY) { LOGW("setForceUse() invalid config %d for FOR_DOCK", config); } + forceVolumeReeval = true; mForceUse[usage] = config; break; default: @@ -388,6 +390,9 @@ void AudioPolicyManagerBase::setForceUse(AudioSystem::force_use usage, AudioSyst #endif updateDeviceForStrategy(); setOutputDevice(mHardwareOutput, newDevice); + if (forceVolumeReeval) { + applyStreamVolumes(mHardwareOutput, newDevice); + } } AudioSystem::forced_config AudioPolicyManagerBase::getForceUse(AudioSystem::force_use usage) diff --git a/libs/audioflinger/AudioResampler.h b/libs/audioflinger/AudioResampler.h index 39656c0..2dfac76 100644 --- a/libs/audioflinger/AudioResampler.h +++ b/libs/audioflinger/AudioResampler.h @@ -76,8 +76,8 @@ protected: int32_t mInSampleRate; AudioBufferProvider::Buffer mBuffer; union { - int16_t mVolume[2]; - uint32_t mVolumeRL; + int16_t mVolume[2]; + uint32_t mVolumeRL; }; int16_t mTargetVolume[2]; format mFormat; diff --git a/libs/utils/ResourceTypes.cpp b/libs/utils/ResourceTypes.cpp index 38d8412..7e0f881 100644 --- a/libs/utils/ResourceTypes.cpp +++ b/libs/utils/ResourceTypes.cpp @@ -625,6 +625,10 @@ void ResXMLParser::restart() mCurNode = NULL; mEventCode = mTree.mError == NO_ERROR ? START_DOCUMENT : BAD_DOCUMENT; } +const ResStringPool& ResXMLParser::getStrings() const +{ + return mTree.mStrings; +} ResXMLParser::event_code_t ResXMLParser::getEventType() const { @@ -1149,11 +1153,6 @@ void ResXMLTree::uninit() restart(); } -const ResStringPool& ResXMLTree::getStrings() const -{ - return mStrings; -} - status_t ResXMLTree::validateNode(const ResXMLTree_node* node) const { const uint16_t eventCode = dtohs(node->header.type); diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java index b0a179f..4fa3327 100644 --- a/media/java/android/media/AudioManager.java +++ b/media/java/android/media/AudioManager.java @@ -1132,41 +1132,31 @@ public class AudioManager { } /** - * TODO unhide for SDK * Used to indicate a loss of audio focus of unknown duration. * @see OnAudioFocusChangeListener#onAudioFocusChanged(int) - * {@hide} */ public static final int AUDIOFOCUS_LOSS = -1; /** - * TODO unhide for SDK * Used to indicate a transient loss of audio focus. * @see OnAudioFocusChangeListener#onAudioFocusChanged(int) - * {@hide} */ public static final int AUDIOFOCUS_LOSS_TRANSIENT = -2; /** - * TODO unhide for SDK * Used to indicate a gain of audio focus, or a request of audio focus, of unknown duration. * @see OnAudioFocusChangeListener#onAudioFocusChanged(int) * @see #requestAudioFocus(OnAudioFocusChangeListener, int, int) - * {@hide} */ public static final int AUDIOFOCUS_GAIN = 1; /** - * TODO unhide for SDK * Used to indicate a temporary gain or request of audio focus, anticipated to last a short * amount of time. Examples of temporary changes are the playback of driving directions, or an * event notification. * @see OnAudioFocusChangeListener#onAudioFocusChanged(int) * @see #requestAudioFocus(OnAudioFocusChangeListener, int, int) - * {@hide} */ public static final int AUDIOFOCUS_GAIN_TRANSIENT = 2; /** - * TODO unhide for SDK - * {@hide} * Interface definition for a callback to be invoked when the audio focus of the system is * updated. */ @@ -1267,8 +1257,6 @@ public class AudioManager { } /** - * TODO unhide for SDK - * {@hide} * Register a listener for audio focus updates. */ public void registerAudioFocusListener(OnAudioFocusChangeListener l) { @@ -1284,9 +1272,7 @@ public class AudioManager { } /** - * TODO unhide for SDK * TODO document for SDK - * {@hide} */ public void unregisterAudioFocusListener(OnAudioFocusChangeListener l) { // notify service to remove it from audio focus stack @@ -1304,22 +1290,16 @@ public class AudioManager { /** - * TODO unhide for SDK * TODO document for SDK - * {@hide} */ public static final int AUDIOFOCUS_REQUEST_FAILED = 0; /** - * TODO unhide for SDK * TODO document for SDK - * {@hide} */ public static final int AUDIOFOCUS_REQUEST_GRANTED = 1; /** - * TODO unhide for SDK - * {@hide} * Request audio focus. * Send a request to obtain the audio focus for a specific stream type * @param l the listener to be notified of audio focus changes @@ -1347,9 +1327,7 @@ public class AudioManager { /** - * TODO unhide for SDK * TODO document for SDK - * {@hide} * Abandon audio focus. * @return {@link #AUDIOFOCUS_REQUEST_FAILED} or {@link #AUDIOFOCUS_REQUEST_GRANTED} */ diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java index e586869..618f239 100644 --- a/media/java/android/media/AudioService.java +++ b/media/java/android/media/AudioService.java @@ -113,9 +113,6 @@ public class AudioService extends IAudioService.Stub { private Object mSettingsLock = new Object(); private boolean mMediaServerOk; - /** cached value of the BT dock address to recognize undocking events */ - private static String sBtDockAddress = ""; - private SoundPool mSoundPool; private Object mSoundEffectsLock = new Object(); private static final int NUM_SOUNDPOOL_CHANNELS = 4; @@ -269,6 +266,7 @@ public class AudioService extends IAudioService.Stub { new IntentFilter(Intent.ACTION_HEADSET_PLUG); intentFilter.addAction(BluetoothA2dp.ACTION_SINK_STATE_CHANGED); intentFilter.addAction(BluetoothHeadset.ACTION_STATE_CHANGED); + intentFilter.addAction(Intent.ACTION_DOCK_EVENT); context.registerReceiver(mReceiver, intentFilter); } @@ -1498,7 +1496,23 @@ public class AudioService extends IAudioService.Stub { public void onReceive(Context context, Intent intent) { String action = intent.getAction(); - if (action.equals(BluetoothA2dp.ACTION_SINK_STATE_CHANGED)) { + if (action.equals(Intent.ACTION_DOCK_EVENT)) { + int dockState = intent.getIntExtra(Intent.EXTRA_DOCK_STATE, + Intent.EXTRA_DOCK_STATE_UNDOCKED); + int config; + switch (dockState) { + case Intent.EXTRA_DOCK_STATE_DESK: + config = AudioSystem.FORCE_BT_DESK_DOCK; + break; + case Intent.EXTRA_DOCK_STATE_CAR: + config = AudioSystem.FORCE_BT_CAR_DOCK; + break; + case Intent.EXTRA_DOCK_STATE_UNDOCKED: + default: + config = AudioSystem.FORCE_NONE; + } + AudioSystem.setForceUse(AudioSystem.FOR_DOCK, config); + } else if (action.equals(BluetoothA2dp.ACTION_SINK_STATE_CHANGED)) { int state = intent.getIntExtra(BluetoothA2dp.EXTRA_SINK_STATE, BluetoothA2dp.STATE_DISCONNECTED); BluetoothDevice btDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); @@ -1508,10 +1522,6 @@ public class AudioService extends IAudioService.Stub { if (isConnected && state != BluetoothA2dp.STATE_CONNECTED && state != BluetoothA2dp.STATE_PLAYING) { - if (address.equals(sBtDockAddress)) { - Log.v(TAG, "Recognized undocking from BT dock"); - AudioSystem.setForceUse(AudioSystem.FOR_DOCK, AudioSystem.FORCE_NONE); - } AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, AudioSystem.DEVICE_STATE_UNAVAILABLE, address); @@ -1519,27 +1529,6 @@ public class AudioService extends IAudioService.Stub { } else if (!isConnected && (state == BluetoothA2dp.STATE_CONNECTED || state == BluetoothA2dp.STATE_PLAYING)) { - if (btDevice.isBluetoothDock()) { - Log.v(TAG, "Recognized connection to BT dock"); - sBtDockAddress = address; - Intent i = context.registerReceiver(null, new IntentFilter(Intent.ACTION_DOCK_EVENT)); - if (i != null) { - int dockState = i.getIntExtra(Intent.EXTRA_DOCK_STATE, Intent.EXTRA_DOCK_STATE_UNDOCKED); - int config; - switch (dockState) { - case Intent.EXTRA_DOCK_STATE_DESK: - config = AudioSystem.FORCE_BT_DESK_DOCK; - break; - case Intent.EXTRA_DOCK_STATE_CAR: - config = AudioSystem.FORCE_BT_CAR_DOCK; - break; - case Intent.EXTRA_DOCK_STATE_UNDOCKED: - default: - config = AudioSystem.FORCE_NONE; - } - AudioSystem.setForceUse(AudioSystem.FOR_DOCK, config); - } - } AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, AudioSystem.DEVICE_STATE_AVAILABLE, address); diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java index 1f02608..1047fa4 100644 --- a/media/java/android/media/MediaScanner.java +++ b/media/java/android/media/MediaScanner.java @@ -630,13 +630,9 @@ public class MediaScanner map.put(Video.Media.ARTIST, (mArtist != null && mArtist.length() > 0 ? mArtist : MediaStore.UNKNOWN_STRING)); map.put(Video.Media.ALBUM, (mAlbum != null && mAlbum.length() > 0 ? mAlbum : MediaStore.UNKNOWN_STRING)); map.put(Video.Media.DURATION, mDuration); - map.put(Video.Media.DATE_TAKEN, mLastModified * 1000); // FIXME - add RESOLUTION } else if (MediaFile.isImageFileType(mFileType)) { // FIXME - add DESCRIPTION - // DATE_TAKEN will be overridden later if this is a JPEG image whose EXIF data - // contains date time information. - map.put(Images.Media.DATE_TAKEN, mLastModified * 1000); } else if (MediaFile.isAudioFileType(mFileType)) { map.put(Audio.Media.ARTIST, (mArtist != null && mArtist.length() > 0 ? mArtist : MediaStore.UNKNOWN_STRING)); map.put(Audio.Media.ALBUM, (mAlbum != null && mAlbum.length() > 0 ? mAlbum : MediaStore.UNKNOWN_STRING)); diff --git a/media/libmedia/mediaplayer.cpp b/media/libmedia/mediaplayer.cpp index d2cec0c..f623295 100644 --- a/media/libmedia/mediaplayer.cpp +++ b/media/libmedia/mediaplayer.cpp @@ -104,7 +104,8 @@ status_t MediaPlayer::setDataSource(const sp<IMediaPlayer>& player) { // scope for the lock Mutex::Autolock _l(mLock); - if ( !( mCurrentState & ( MEDIA_PLAYER_IDLE | MEDIA_PLAYER_STATE_ERROR ) ) ) { + if ( !( (mCurrentState & MEDIA_PLAYER_IDLE) || + (mCurrentState == MEDIA_PLAYER_STATE_ERROR ) ) ) { LOGE("setDataSource called in state %d", mCurrentState); return INVALID_OPERATION; } diff --git a/media/libstagefright/StagefrightMediaScanner.cpp b/media/libstagefright/StagefrightMediaScanner.cpp index b1eca2b..34fb2bc 100644 --- a/media/libstagefright/StagefrightMediaScanner.cpp +++ b/media/libstagefright/StagefrightMediaScanner.cpp @@ -20,7 +20,8 @@ #include <media/stagefright/StagefrightMediaScanner.h> -#include "include/StagefrightMetadataRetriever.h" +#include <media/mediametadataretriever.h> +#include <private/media/VideoFrame.h> // Sonivox includes #include <libsonivox/eas.h> @@ -32,7 +33,7 @@ namespace android { StagefrightMediaScanner::StagefrightMediaScanner() - : mRetriever(new StagefrightMetadataRetriever) { + : mRetriever(new MediaMetadataRetriever) { } StagefrightMediaScanner::~StagefrightMediaScanner() {} @@ -146,6 +147,8 @@ failure: status_t StagefrightMediaScanner::processFile( const char *path, const char *mimeType, MediaScannerClient &client) { + LOGV("processFile '%s'.", path); + client.setLocale(locale()); client.beginFile(); @@ -218,6 +221,8 @@ status_t StagefrightMediaScanner::processFile( } char *StagefrightMediaScanner::extractAlbumArt(int fd) { + LOGV("extractAlbumArt %d", fd); + off_t size = lseek(fd, 0, SEEK_END); if (size < 0) { return NULL; @@ -227,15 +232,14 @@ char *StagefrightMediaScanner::extractAlbumArt(int fd) { if (mRetriever->setDataSource(fd, 0, size) == OK && mRetriever->setMode( METADATA_MODE_FRAME_CAPTURE_ONLY) == OK) { - MediaAlbumArt *art = mRetriever->extractAlbumArt(); + sp<IMemory> mem = mRetriever->extractAlbumArt(); + + if (mem != NULL) { + MediaAlbumArt *art = static_cast<MediaAlbumArt *>(mem->pointer()); - if (art != NULL) { char *data = (char *)malloc(art->mSize + 4); *(int32_t *)data = art->mSize; - memcpy(&data[4], art->mData, art->mSize); - - delete art; - art = NULL; + memcpy(&data[4], &art[1], art->mSize); return data; } diff --git a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java index 02e1f07..4635f48 100644 --- a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java +++ b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java @@ -5,6 +5,7 @@ import com.android.internal.content.PackageHelper; import android.content.Intent; import android.content.pm.IPackageManager; import android.content.pm.PackageInfo; +import android.content.pm.PackageInfoLite; import android.content.pm.PackageManager; import android.content.pm.PackageParser; import android.content.pm.PackageParser.Package; @@ -28,6 +29,7 @@ import java.io.IOException; import java.io.InputStream; import android.os.FileUtils; +import android.os.storage.IMountService; import android.provider.Settings; /* @@ -86,46 +88,51 @@ public class DefaultContainerService extends IntentService { * specified by file uri location. * @param fileUri the uri of resource to be copied. Should be a * file uri - * @return Returns - * PackageHelper.RECOMMEND_INSTALL_INTERNAL to install on internal storage - * PackageHelper.RECOMMEND_INSTALL_EXTERNAL to install on external media - * PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE for storage errors - * PackageHelper.RECOMMEND_FAILED_INVALID_APK for parse errors. + * @return Returns PackageInfoLite object containing + * the package info and recommended app location. */ - public int getRecommendedInstallLocation(final Uri fileUri) { + public PackageInfoLite getMinimalPackageInfo(final Uri fileUri) { + PackageInfoLite ret = new PackageInfoLite(); if (fileUri == null) { Log.i(TAG, "Invalid package uri " + fileUri); - return PackageHelper.RECOMMEND_FAILED_INVALID_APK; + ret.recommendedInstallLocation = PackageHelper.RECOMMEND_FAILED_INVALID_APK; + return ret; } String scheme = fileUri.getScheme(); if (scheme != null && !scheme.equals("file")) { Log.w(TAG, "Falling back to installing on internal storage only"); - return PackageHelper.RECOMMEND_INSTALL_INTERNAL; + ret.recommendedInstallLocation = PackageHelper.RECOMMEND_INSTALL_INTERNAL; + return ret; } String archiveFilePath = fileUri.getPath(); PackageParser packageParser = new PackageParser(archiveFilePath); File sourceFile = new File(archiveFilePath); DisplayMetrics metrics = new DisplayMetrics(); metrics.setToDefaults(); - PackageParser.Package pkg = packageParser.parsePackage(sourceFile, - archiveFilePath, metrics, 0); + PackageParser.PackageLite pkg = packageParser.parsePackageLite( + archiveFilePath, 0); + ret.packageName = pkg.packageName; + ret.installLocation = pkg.installLocation; // Nuke the parser reference right away and force a gc Runtime.getRuntime().gc(); packageParser = null; if (pkg == null) { Log.w(TAG, "Failed to parse package"); - return PackageHelper.RECOMMEND_FAILED_INVALID_APK; + ret.recommendedInstallLocation = PackageHelper.RECOMMEND_FAILED_INVALID_APK; + return ret; } - int loc = recommendAppInstallLocation(pkg); + ret.packageName = pkg.packageName; + int loc = recommendAppInstallLocation(pkg.installLocation, archiveFilePath); if (loc == PackageManager.INSTALL_EXTERNAL) { - return PackageHelper.RECOMMEND_INSTALL_EXTERNAL; + ret.recommendedInstallLocation = PackageHelper.RECOMMEND_INSTALL_EXTERNAL; } else if (loc == ERR_LOC) { Log.i(TAG, "Failed to install insufficient storage"); - return PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE; + ret.recommendedInstallLocation = PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE; } else { // Implies install on internal storage. - return PackageHelper.RECOMMEND_INSTALL_INTERNAL; + ret.recommendedInstallLocation = PackageHelper.RECOMMEND_INSTALL_INTERNAL; } + return ret; } }; @@ -171,62 +178,37 @@ public class DefaultContainerService extends IntentService { String codePath = packageURI.getPath(); File codeFile = new File(codePath); String newCachePath = null; - final int CREATE_FAILED = 1; - final int COPY_FAILED = 2; - final int FINALIZE_FAILED = 3; - final int PASS = 4; - int errCode = CREATE_FAILED; // Create new container if ((newCachePath = PackageHelper.createSdDir(codeFile, - newCid, key, Process.myUid())) != null) { - if (localLOGV) Log.i(TAG, "Created container for " + newCid - + " at path : " + newCachePath); - File resFile = new File(newCachePath, resFileName); - errCode = COPY_FAILED; - // Copy file from codePath - if (FileUtils.copyFile(new File(codePath), resFile)) { - if (localLOGV) Log.i(TAG, "Copied " + codePath + " to " + resFile); - errCode = FINALIZE_FAILED; - if (PackageHelper.finalizeSdDir(newCid)) { - if (localLOGV) Log.i(TAG, "Finalized container " + newCid); - errCode = PASS; - } - } - } - // Print error based on errCode - String errMsg = ""; - switch (errCode) { - case CREATE_FAILED: - errMsg = "CREATE_FAILED"; - break; - case COPY_FAILED: - errMsg = "COPY_FAILED"; - if (localLOGV) Log.i(TAG, "Destroying " + newCid + - " at path " + newCachePath + " after " + errMsg); - PackageHelper.destroySdDir(newCid); - break; - case FINALIZE_FAILED: - errMsg = "FINALIZE_FAILED"; - if (localLOGV) Log.i(TAG, "Destroying " + newCid + - " at path " + newCachePath + " after " + errMsg); - PackageHelper.destroySdDir(newCid); - break; - default: - errMsg = "PASS"; - if (PackageHelper.isContainerMounted(newCid)) { - if (localLOGV) Log.i(TAG, "Unmounting " + newCid + - " at path " + newCachePath + " after " + errMsg); - // Force a gc to avoid being killed. - Runtime.getRuntime().gc(); - PackageHelper.unMountSdDir(newCid); - } else { - if (localLOGV) Log.i(TAG, "Container " + newCid + " not mounted"); - } - break; + newCid, key, Process.myUid())) == null) { + Log.e(TAG, "Failed to create container " + newCid); + return null; } - if (errCode != PASS) { + if (localLOGV) Log.i(TAG, "Created container for " + newCid + + " at path : " + newCachePath); + File resFile = new File(newCachePath, resFileName); + if (!FileUtils.copyFile(new File(codePath), resFile)) { + Log.e(TAG, "Failed to copy " + codePath + " to " + resFile); + // Clean up container + PackageHelper.destroySdDir(newCid); return null; } + if (localLOGV) Log.i(TAG, "Copied " + codePath + " to " + resFile); + if (!PackageHelper.finalizeSdDir(newCid)) { + Log.e(TAG, "Failed to finalize " + newCid + " at path " + newCachePath); + // Clean up container + PackageHelper.destroySdDir(newCid); + } + if (localLOGV) Log.i(TAG, "Finalized container " + newCid); + if (PackageHelper.isContainerMounted(newCid)) { + if (localLOGV) Log.i(TAG, "Unmounting " + newCid + + " at path " + newCachePath); + // Force a gc to avoid being killed. + Runtime.getRuntime().gc(); + PackageHelper.unMountSdDir(newCid); + } else { + if (localLOGV) Log.i(TAG, "Container " + newCid + " not mounted"); + } return newCachePath; } @@ -307,29 +289,28 @@ public class DefaultContainerService extends IntentService { private static final long INSTALL_ON_SD_THRESHOLD = (1024 * 1024); private static final int ERR_LOC = -1; - public int recommendAppInstallLocation(Package pkg) { + private int recommendAppInstallLocation(int installLocation, + String archiveFilePath) { // Initial implementation: // Package size = code size + cache size + data size // If code size > 1 MB, install on SD card. // Else install on internal NAND flash, unless space on NAND is less than 10% - - if (pkg == null) { - return ERR_LOC; + String status = Environment.getExternalStorageState(); + long availSDSize = -1; + if (status.equals(Environment.MEDIA_MOUNTED)) { + StatFs sdStats = new StatFs( + Environment.getExternalStorageDirectory().getPath()); + availSDSize = (long)sdStats.getAvailableBlocks() * + (long)sdStats.getBlockSize(); } + StatFs internalStats = new StatFs(Environment.getDataDirectory().getPath()); + long totalInternalSize = (long)internalStats.getBlockCount() * + (long)internalStats.getBlockSize(); + long availInternalSize = (long)internalStats.getAvailableBlocks() * + (long)internalStats.getBlockSize(); - StatFs internalFlashStats = new StatFs(Environment.getDataDirectory().getPath()); - StatFs sdcardStats = new StatFs(Environment.getExternalStorageDirectory().getPath()); - - long totalInternalFlashSize = (long)internalFlashStats.getBlockCount() * - (long)internalFlashStats.getBlockSize(); - long availInternalFlashSize = (long)internalFlashStats.getAvailableBlocks() * - (long)internalFlashStats.getBlockSize(); - long availSDSize = (long)sdcardStats.getAvailableBlocks() * - (long)sdcardStats.getBlockSize(); - - double pctNandFree = (double)availInternalFlashSize / (double)totalInternalFlashSize; + double pctNandFree = (double)availInternalSize / (double)totalInternalSize; - final String archiveFilePath = pkg.mScanPath; File apkFile = new File(archiveFilePath); long pkgLen = apkFile.length(); @@ -339,15 +320,15 @@ public class DefaultContainerService extends IntentService { // For dex files. Just ignore and fail when extracting. Max limit of 2Gig for now. long reqInternalSize = 0; boolean intThresholdOk = (pctNandFree >= LOW_NAND_FLASH_TRESHOLD); - boolean intAvailOk = ((reqInstallSize + reqInternalSize) < availInternalFlashSize); + boolean intAvailOk = ((reqInstallSize + reqInternalSize) < availInternalSize); boolean fitsOnSd = (reqInstallSize < availSDSize) && intThresholdOk && - (reqInternalSize < availInternalFlashSize); + (reqInternalSize < availInternalSize); boolean fitsOnInt = intThresholdOk && intAvailOk; // Consider application flags preferences as well... - boolean installOnlyOnSd = (pkg.installLocation == + boolean installOnlyOnSd = (installLocation == PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL); - boolean installOnlyInternal = (pkg.installLocation == + boolean installOnlyInternal = (installLocation == PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY); if (installOnlyInternal) { // If set explicitly in manifest, diff --git a/packages/SettingsProvider/etc/Android.mk b/packages/SettingsProvider/etc/Android.mk deleted file mode 100644 index 81ca4b1..0000000 --- a/packages/SettingsProvider/etc/Android.mk +++ /dev/null @@ -1,33 +0,0 @@ -# -# Copyright (C) 2008 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. -# - -LOCAL_PATH := $(my-dir) - -######################## -include $(CLEAR_VARS) - -LOCAL_MODULE := bookmarks.xml - -LOCAL_MODULE_TAGS := user - -# This will install the file in /system/etc -# -LOCAL_MODULE_CLASS := ETC - -LOCAL_SRC_FILES := $(LOCAL_MODULE) - -include $(BUILD_PREBUILT) - diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml index bd131e0..64bb0ef 100644 --- a/packages/SettingsProvider/res/values/defaults.xml +++ b/packages/SettingsProvider/res/values/defaults.xml @@ -70,4 +70,7 @@ <string name="def_lock_sound" translatable="false">/system/media/audio/ui/Lock.ogg</string> <string name="def_unlock_sound" translatable="false">/system/media/audio/ui/Unlock.ogg</string> + <!-- Default for Settings.System.VIBRATE_IN_SILENT --> + <bool name="def_vibrate_in_silent">true</bool> + </resources> diff --git a/packages/SettingsProvider/etc/bookmarks.xml b/packages/SettingsProvider/res/xml/bookmarks.xml index 734e0cd..dfaeeaf 100644 --- a/packages/SettingsProvider/etc/bookmarks.xml +++ b/packages/SettingsProvider/res/xml/bookmarks.xml @@ -32,8 +32,8 @@ class="com.google.android.gm.ConversationListActivityGmail" shortcut="g" /> <bookmark - package="com.android.providers.im" - class="com.android.providers.im.LandingPage" + package="com.android.im" + class="com.android.im.app.LandingPage" shortcut="i" /> <bookmark package="com.android.calendar" @@ -53,8 +53,4 @@ package="com.android.mms" class="com.android.mms.ui.ConversationList" shortcut="s" /> - <bookmark - package="com.google.android.youtube" - class="com.google.android.youtube.HomeActivity" - shortcut="y" /> </bookmarks> diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java index bccf7d2..edf3923 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java @@ -23,40 +23,29 @@ import android.content.Intent; import android.content.pm.ActivityInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; -import android.content.pm.ResolveInfo; -import android.content.pm.ServiceInfo; -import android.content.res.Resources; +import android.content.res.XmlResourceParser; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; -import android.database.sqlite.SQLiteDoneException; -import android.database.sqlite.SQLiteException; import android.database.sqlite.SQLiteOpenHelper; import android.database.sqlite.SQLiteStatement; import android.media.AudioManager; import android.media.AudioService; import android.net.ConnectivityManager; -import android.os.Environment; import android.os.SystemProperties; import android.provider.Settings; import android.provider.Settings.Secure; -import android.speech.RecognitionService; -import android.speech.RecognizerIntent; import android.text.TextUtils; +import android.util.AttributeSet; import android.util.Config; import android.util.Log; import android.util.Xml; import com.android.internal.telephony.RILConstants; import com.android.internal.util.XmlUtils; - import com.android.internal.widget.LockPatternUtils; import com.android.internal.widget.LockPatternView; - import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileReader; import java.io.IOException; import java.util.List; @@ -65,11 +54,6 @@ import java.util.List; * Mostly just has a bit {@link #onCreate} to initialize the database. */ public class DatabaseHelper extends SQLiteOpenHelper { - /** - * Path to file containing default bookmarks, relative to ANDROID_ROOT. - */ - private static final String DEFAULT_BOOKMARKS_PATH = "etc/bookmarks.xml"; - private static final String TAG = "SettingsProvider"; private static final String DATABASE_NAME = "settings.db"; @@ -77,7 +61,7 @@ public class DatabaseHelper extends SQLiteOpenHelper { // database gets upgraded properly. At a minimum, please confirm that 'upgradeVersion' // is properly propagated through your change. Not doing so will result in a loss of user // settings. - private static final int DATABASE_VERSION = 52; + private static final int DATABASE_VERSION = 53; private Context mContext; @@ -158,7 +142,6 @@ public class DatabaseHelper extends SQLiteOpenHelper { * notification vibrate to on. */ loadVibrateSetting(db, true); - if (Config.LOGD) Log.d(TAG, "Reset system vibrate setting"); upgradeVersion = 21; } @@ -651,7 +634,26 @@ public class DatabaseHelper extends SQLiteOpenHelper { upgradeVersion = 52; } - if (upgradeVersion != currentVersion) { + if (upgradeVersion == 52) { + // new vibration/silent mode settings + db.beginTransaction(); + try { + SQLiteStatement stmt = db.compileStatement("INSERT INTO system(name,value)" + + " VALUES(?,?);"); + loadBooleanSetting(stmt, Settings.System.VIBRATE_IN_SILENT, + R.bool.def_vibrate_in_silent); + stmt.close(); + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); + } + + upgradeVersion = 53; + } + + // *** Remember to update DATABASE_VERSION above! + + if (upgradeVersion != currentVersion) { Log.w(TAG, "Got stuck trying to upgrade from version " + upgradeVersion + ", must wipe the settings provider"); db.execSQL("DROP TABLE IF EXISTS system"); @@ -735,39 +737,28 @@ public class DatabaseHelper extends SQLiteOpenHelper { * * @param db The database to write the values into * @param startingIndex The zero-based position at which bookmarks in this file should begin - * @param subPath The relative path from ANDROID_ROOT to the file to read - * @param quiet If true, do no complain if the file is missing */ - private int loadBookmarks(SQLiteDatabase db, int startingIndex, String subPath, - boolean quiet) { - FileReader bookmarksReader; - - // Environment.getRootDirectory() is a fancy way of saying ANDROID_ROOT or "/system". - final File favFile = new File(Environment.getRootDirectory(), subPath); - try { - bookmarksReader = new FileReader(favFile); - } catch (FileNotFoundException e) { - if (!quiet) { - Log.e(TAG, "Couldn't find or open bookmarks file " + favFile); - } - return 0; - } - + private int loadBookmarks(SQLiteDatabase db, int startingIndex) { Intent intent = new Intent(Intent.ACTION_MAIN, null); intent.addCategory(Intent.CATEGORY_LAUNCHER); ContentValues values = new ContentValues(); PackageManager packageManager = mContext.getPackageManager(); - ActivityInfo info; int i = startingIndex; - try { - XmlPullParser parser = Xml.newPullParser(); - parser.setInput(bookmarksReader); + try { + XmlResourceParser parser = mContext.getResources().getXml(R.xml.bookmarks); XmlUtils.beginDocument(parser, "bookmarks"); - while (true) { - XmlUtils.nextElement(parser); + final int depth = parser.getDepth(); + int type; + + while (((type = parser.next()) != XmlPullParser.END_TAG || + parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) { + + if (type != XmlPullParser.START_TAG) { + continue; + } String name = parser.getName(); if (!"bookmark".equals(name)) { @@ -777,23 +768,36 @@ public class DatabaseHelper extends SQLiteOpenHelper { String pkg = parser.getAttributeValue(null, "package"); String cls = parser.getAttributeValue(null, "class"); String shortcutStr = parser.getAttributeValue(null, "shortcut"); + int shortcutValue = (int) shortcutStr.charAt(0); if (TextUtils.isEmpty(shortcutStr)) { Log.w(TAG, "Unable to get shortcut for: " + pkg + "/" + cls); } + + ActivityInfo info = null; + ComponentName cn = new ComponentName(pkg, cls); try { - ComponentName cn = new ComponentName(pkg, cls); info = packageManager.getActivityInfo(cn, 0); + } catch (PackageManager.NameNotFoundException e) { + String[] packages = packageManager.canonicalToCurrentPackageNames( + new String[] { pkg }); + cn = new ComponentName(packages[0], cls); + try { + info = packageManager.getActivityInfo(cn, 0); + } catch (PackageManager.NameNotFoundException e1) { + Log.w(TAG, "Unable to add bookmark: " + pkg + "/" + cls, e); + } + } + + if (info != null) { intent.setComponent(cn); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - values.put(Settings.Bookmarks.INTENT, intent.toURI()); + values.put(Settings.Bookmarks.INTENT, intent.toUri(0)); values.put(Settings.Bookmarks.TITLE, info.loadLabel(packageManager).toString()); values.put(Settings.Bookmarks.SHORTCUT, shortcutValue); db.insert("bookmarks", null, values); i++; - } catch (PackageManager.NameNotFoundException e) { - Log.w(TAG, "Unable to add bookmark: " + pkg + "/" + cls, e); } } } catch (XmlPullParserException e) { @@ -811,7 +815,7 @@ public class DatabaseHelper extends SQLiteOpenHelper { * @param db The database to write the values into */ private void loadBookmarks(SQLiteDatabase db) { - loadBookmarks(db, 0, DEFAULT_BOOKMARKS_PATH, false); + loadBookmarks(db, 0); } /** @@ -890,8 +894,6 @@ public class DatabaseHelper extends SQLiteOpenHelper { SQLiteStatement stmt = db.compileStatement("INSERT OR IGNORE INTO system(name,value)" + " VALUES(?,?);"); - Resources r = mContext.getResources(); - loadBooleanSetting(stmt, Settings.System.DIM_SCREEN, R.bool.def_dim_screen); loadSetting(stmt, Settings.System.STAY_ON_WHILE_PLUGGED_IN, @@ -947,6 +949,9 @@ public class DatabaseHelper extends SQLiteOpenHelper { loadUISoundEffectsSettings(stmt); + loadBooleanSetting(stmt, Settings.System.VIBRATE_IN_SILENT, + R.bool.def_vibrate_in_silent); + stmt.close(); } diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java index 942d32d..1b4ba81 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java @@ -60,6 +60,11 @@ public class SettingsProvider extends ContentProvider { private static final SettingsCache sSystemCache = new SettingsCache(); private static final SettingsCache sSecureCache = new SettingsCache(); + // Over this size we don't reject loading or saving settings but + // we do consider them broken/malicious and don't keep them in + // memory at least: + private static final int MAX_CACHE_ENTRY_SIZE = 500; + private static final Bundle NULL_SETTING = Bundle.forPair("value", null); protected DatabaseHelper mOpenHelper; @@ -264,10 +269,7 @@ public class SettingsProvider extends ContentProvider { null, null, null, null); if (cursor != null && cursor.getCount() == 1) { cursor.moveToFirst(); - String value = cursor.getString(0); - Bundle bundle = (value == null) ? NULL_SETTING : Bundle.forPair("value", value); - cache.putIfAbsentLocked(key, bundle); - return bundle; + return cache.putIfAbsent(key, cursor.getString(0)); } } catch (SQLiteException e) { Log.w(TAG, "settings lookup error", e); @@ -275,7 +277,7 @@ public class SettingsProvider extends ContentProvider { } finally { if (cursor != null) cursor.close(); } - cache.putIfAbsentLocked(key, NULL_SETTING); + cache.putIfAbsent(key, null); return NULL_SETTING; } @@ -434,11 +436,16 @@ public class SettingsProvider extends ContentProvider { if (!parseProviderList(url, initialValues)) return null; } + SettingsCache cache = SettingsCache.forTable(args.table); + String value = initialValues.getAsString(Settings.NameValueTable.VALUE); + if (SettingsCache.isRedundantSetValue(cache, name, value)) { + return Uri.withAppendedPath(url, name); + } + SQLiteDatabase db = mOpenHelper.getWritableDatabase(); final long rowId = db.insert(args.table, null, initialValues); if (rowId <= 0) return null; - SettingsCache cache = SettingsCache.forTable(args.table); SettingsCache.populate(cache, initialValues); // before we notify if (LOCAL_LOGV) Log.v(TAG, args.table + " <- " + initialValues); @@ -592,6 +599,7 @@ public class SettingsProvider extends ContentProvider { * database. */ private static final class SettingsCache extends LinkedHashMap<String, Bundle> { + public SettingsCache() { super(MAX_CACHE_ENTRIES, 0.75f /* load factor */, true /* access ordered */); } @@ -601,14 +609,23 @@ public class SettingsProvider extends ContentProvider { return size() > MAX_CACHE_ENTRIES; } - public void putIfAbsentLocked(String key, Bundle value) { - synchronized (this) { - if (containsKey(key)) { - // Lost a race. - return; + /** + * Atomic cache population, conditional on size of value and if + * we lost a race. + * + * @returns a Bundle to send back to the client from call(), even + * if we lost the race. + */ + public Bundle putIfAbsent(String key, String value) { + Bundle bundle = (value == null) ? NULL_SETTING : Bundle.forPair("value", value); + if (value == null || value.length() <= MAX_CACHE_ENTRY_SIZE) { + synchronized (this) { + if (!containsKey(key)) { + put(key, bundle); + } } - put(key, value); } + return bundle; } public static SettingsCache forTable(String tableName) { @@ -635,7 +652,11 @@ public class SettingsProvider extends ContentProvider { } String value = contentValues.getAsString(Settings.NameValueTable.VALUE); synchronized (cache) { - cache.put(name, Bundle.forPair(Settings.NameValueTable.VALUE, value)); + if (value == null || value.length() <= MAX_CACHE_ENTRY_SIZE) { + cache.put(name, Bundle.forPair(Settings.NameValueTable.VALUE, value)); + } else { + cache.remove(name); + } } } @@ -653,5 +674,21 @@ public class SettingsProvider extends ContentProvider { } } + /** + * For suppressing duplicate/redundant settings inserts early, + * checking our cache first (but without faulting it in), + * before going to sqlite with the mutation. + */ + public static boolean isRedundantSetValue(SettingsCache cache, String name, String value) { + if (cache == null) return false; + synchronized (cache) { + Bundle bundle = cache.get(name); + if (bundle == null) return false; + String oldValue = bundle.getPairValue(); + if (oldValue == null && value == null) return true; + if ((oldValue == null) != (value == null)) return false; + return oldValue.equals(value); + } + } } } diff --git a/packages/TtsService/src/android/tts/TtsService.java b/packages/TtsService/src/android/tts/TtsService.java index 7f76425..6b7ab3f 100755 --- a/packages/TtsService/src/android/tts/TtsService.java +++ b/packages/TtsService/src/android/tts/TtsService.java @@ -38,6 +38,7 @@ import android.speech.tts.TextToSpeech; import android.util.Log; import java.io.File; +import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; @@ -1112,7 +1113,7 @@ public class TtsService extends Service implements OnCompletionListener { * @param filename * The string that gives the full output filename; it should be * something like "/sdcard/myappsounds/mysound.wav". - * @return A boolean that indicates if the synthesis succeeded + * @return A boolean that indicates if the synthesis can be started */ private boolean synthesizeToFile(String callingApp, String text, ArrayList<String> params, String filename) { @@ -1125,6 +1126,22 @@ public class TtsService extends Service implements OnCompletionListener { if (text.length() >= MAX_SPEECH_ITEM_CHAR_LENGTH){ return false; } + // Check that the output file can be created + try { + File tempFile = new File(filename); + if (tempFile.exists()) { + Log.v("TtsService", "File " + filename + " exists, deleting."); + tempFile.delete(); + } + if (!tempFile.createNewFile()) { + Log.e("TtsService", "Unable to synthesize to file: can't create " + filename); + return false; + } + tempFile.delete(); + } catch (IOException e) { + Log.e("TtsService", "Can't create " + filename + " due to exception " + e); + return false; + } mSpeechQueue.add(new SpeechItem(callingApp, text, params, SpeechItem.TEXT_TO_FILE, filename)); if (!mIsSpeaking) { processSpeechQueue(); diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java index a1c45dc..70deb3e 100644 --- a/services/java/com/android/server/ConnectivityService.java +++ b/services/java/com/android/server/ConnectivityService.java @@ -995,6 +995,8 @@ public class ConnectivityService extends IConnectivityManager.Stub { } } else { newNet.reconnect(); + newNet = null; // not officially avail.. try anyway, but + // report no failover } } } diff --git a/services/java/com/android/server/MountService.java b/services/java/com/android/server/MountService.java index 0974f7f..713358d 100644 --- a/services/java/com/android/server/MountService.java +++ b/services/java/com/android/server/MountService.java @@ -26,6 +26,7 @@ import android.content.pm.PackageManager; import android.net.Uri; import android.os.storage.IMountService; import android.os.storage.IMountServiceListener; +import android.os.storage.IMountShutdownObserver; import android.os.storage.StorageResultCode; import android.os.Handler; import android.os.Message; @@ -172,69 +173,110 @@ class MountService extends IMountService.Stub } } + class ShutdownCallBack extends UnmountCallBack { + IMountShutdownObserver observer; + ShutdownCallBack(String path, IMountShutdownObserver observer) { + super(path, true); + this.observer = observer; + } + + @Override + void handleFinished() { + int ret = doUnmountVolume(path, true); + if (observer != null) { + try { + observer.onShutDownComplete(ret); + } catch (RemoteException e) { + Log.w(TAG, "RemoteException when shutting down"); + } + } + } + } + final private Handler mHandler = new Handler() { ArrayList<UnmountCallBack> mForceUnmounts = new ArrayList<UnmountCallBack>(); + boolean mRegistered = false; + + void registerReceiver() { + mRegistered = true; + mContext.registerReceiver(mPmReceiver, mPmFilter); + } + + void unregisterReceiver() { + mRegistered = false; + mContext.unregisterReceiver(mPmReceiver); + } public void handleMessage(Message msg) { switch (msg.what) { case H_UNMOUNT_PM_UPDATE: { UnmountCallBack ucb = (UnmountCallBack) msg.obj; mForceUnmounts.add(ucb); - mContext.registerReceiver(mPmReceiver, mPmFilter); - boolean hasExtPkgs = mPms.updateExternalMediaStatus(false); - if (!hasExtPkgs) { - // Unregister right away - mHandler.sendEmptyMessage(H_UNMOUNT_PM_DONE); + // Register only if needed. + if (!mRegistered) { + registerReceiver(); + boolean hasExtPkgs = mPms.updateExternalMediaStatus(false); + if (!hasExtPkgs) { + // Unregister right away + mHandler.sendEmptyMessage(H_UNMOUNT_PM_DONE); + } } break; } case H_UNMOUNT_PM_DONE: { - // Unregister receiver - mContext.unregisterReceiver(mPmReceiver); - UnmountCallBack ucb = mForceUnmounts.get(0); - if (ucb == null || ucb.path == null) { - // Just ignore - return; + // Unregister now. + if (mRegistered) { + unregisterReceiver(); } - String path = ucb.path; - boolean done = false; - if (!ucb.force) { - done = true; - } else { - int pids[] = getStorageUsers(path); - if (pids == null || pids.length == 0) { + int size = mForceUnmounts.size(); + int sizeArr[] = new int[size]; + int sizeArrN = 0; + for (int i = 0; i < size; i++) { + UnmountCallBack ucb = mForceUnmounts.get(i); + String path = ucb.path; + boolean done = false; + if (!ucb.force) { done = true; } else { - // Kill processes holding references first - ActivityManagerService ams = (ActivityManagerService) - ServiceManager.getService("activity"); - // Eliminate system process here? - boolean ret = ams.killPidsForMemory(pids); - if (ret) { - // Confirm if file references have been freed. - pids = getStorageUsers(path); - if (pids == null || pids.length == 0) { - done = true; + int pids[] = getStorageUsers(path); + if (pids == null || pids.length == 0) { + done = true; + } else { + // Kill processes holding references first + ActivityManagerService ams = (ActivityManagerService) + ServiceManager.getService("activity"); + // Eliminate system process here? + boolean ret = ams.killPidsForMemory(pids); + if (ret) { + // Confirm if file references have been freed. + pids = getStorageUsers(path); + if (pids == null || pids.length == 0) { + done = true; + } } } } - } - if (done) { - mForceUnmounts.remove(0); - mHandler.sendMessage(mHandler.obtainMessage(H_UNMOUNT_MS, - ucb)); - } else { - if (ucb.retries >= MAX_UNMOUNT_RETRIES) { - Log.i(TAG, "Cannot unmount inspite of " + - MAX_UNMOUNT_RETRIES + " to unmount media"); - // Send final broadcast indicating failure to unmount. + if (done) { + sizeArr[sizeArrN++] = i; + mHandler.sendMessage(mHandler.obtainMessage(H_UNMOUNT_MS, + ucb)); } else { - mHandler.sendMessageDelayed( - mHandler.obtainMessage(H_UNMOUNT_PM_DONE, - ucb.retries++), - RETRY_UNMOUNT_DELAY); + if (ucb.retries >= MAX_UNMOUNT_RETRIES) { + Log.i(TAG, "Cannot unmount inspite of " + + MAX_UNMOUNT_RETRIES + " to unmount media"); + // Send final broadcast indicating failure to unmount. + } else { + mHandler.sendMessageDelayed( + mHandler.obtainMessage(H_UNMOUNT_PM_DONE, + ucb.retries++), + RETRY_UNMOUNT_DELAY); + } } } + // Remove already processed elements from list. + for (int i = (sizeArrN-1); i >= 0; i--) { + mForceUnmounts.remove(sizeArr[i]); + } break; } case H_UNMOUNT_MS : { @@ -826,7 +868,7 @@ class MountService extends IMountService.Stub } } - public void shutdown() { + public void shutdown(final IMountShutdownObserver observer) { validatePermission(android.Manifest.permission.SHUTDOWN); Log.i(TAG, "Shutting down"); @@ -865,12 +907,9 @@ class MountService extends IMountService.Stub } if (state.equals(Environment.MEDIA_MOUNTED)) { - /* - * If the media is mounted, then gracefully unmount it. - */ - if (doUnmountVolume(path, true) != StorageResultCode.OperationSucceeded) { - Log.e(TAG, "Failed to unmount media for shutdown"); - } + // Post a unmount message. + ShutdownCallBack ucb = new ShutdownCallBack(path, observer); + mHandler.sendMessage(mHandler.obtainMessage(H_UNMOUNT_PM_UPDATE, ucb)); } } diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java index 1ff0244..4c1356b 100644 --- a/services/java/com/android/server/PackageManagerService.java +++ b/services/java/com/android/server/PackageManagerService.java @@ -51,6 +51,7 @@ import android.content.pm.IPackageMoveObserver; import android.content.pm.IPackageStatsObserver; import android.content.pm.InstrumentationInfo; import android.content.pm.PackageInfo; +import android.content.pm.PackageInfoLite; import android.content.pm.PackageManager; import android.content.pm.PackageStats; import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT; @@ -78,12 +79,14 @@ import android.os.Environment; import android.os.FileObserver; import android.os.FileUtils; import android.os.Handler; +import android.os.StatFs; import android.os.storage.StorageResultCode; import android.os.ParcelFileDescriptor; import android.os.Process; import android.os.ServiceManager; import android.os.SystemClock; import android.os.SystemProperties; +import android.provider.Settings; import android.security.SystemKeyStore; import android.util.*; import android.view.Display; @@ -4568,21 +4571,79 @@ class PackageManagerService extends IPackageManager.Stub { this.installerPackageName = installerPackageName; } + private int installLocationPolicy(PackageInfoLite pkgLite, int flags) { + String packageName = pkgLite.packageName; + int installLocation = pkgLite.installLocation; + boolean onSd = (flags & PackageManager.INSTALL_EXTERNAL) != 0; + synchronized (mPackages) { + PackageParser.Package pkg = mPackages.get(packageName); + if (pkg != null) { + if ((flags & PackageManager.INSTALL_REPLACE_EXISTING) != 0) { + // Check for updated system application. + if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { + if (onSd) { + Log.w(TAG, "Cannot install update to system app on sdcard"); + return PackageHelper.RECOMMEND_FAILED_INVALID_LOCATION; + } + return PackageHelper.RECOMMEND_INSTALL_INTERNAL; + } else { + // When replacing apps make sure we honour + // the existing app location if not overwritten by other options + boolean prevOnSd = (pkg.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0; + if (onSd) { + // Install flag overrides everything. + return PackageHelper.RECOMMEND_INSTALL_EXTERNAL; + } + // If current upgrade does not specify install location. + if (installLocation == PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY) { + // Application explicitly specified internal. + return PackageHelper.RECOMMEND_INSTALL_INTERNAL; + } else if (installLocation == PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL) { + // App explictly prefers external. Let policy decide + } else if (installLocation == PackageInfo.INSTALL_LOCATION_AUTO) { + // Prefer previous location + return prevOnSd ? PackageHelper.RECOMMEND_INSTALL_EXTERNAL: + PackageHelper.RECOMMEND_INSTALL_INTERNAL; + } + } + } else { + // Invalid install. Return error code + return PackageHelper.RECOMMEND_FAILED_ALREADY_EXISTS; + } + } + } + // All the special cases have been taken care of. + // Return result based on recommended install location. + if (onSd) { + return PackageHelper.RECOMMEND_INSTALL_EXTERNAL; + } + return pkgLite.recommendedInstallLocation; + } + public void handleStartCopy() throws RemoteException { int ret = PackageManager.INSTALL_SUCCEEDED; + boolean fwdLocked = (flags & PackageManager.INSTALL_FORWARD_LOCK) != 0; + boolean onSd = (flags & PackageManager.INSTALL_EXTERNAL) != 0; // Dont need to invoke getInstallLocation for forward locked apps. - if ((flags & PackageManager.INSTALL_FORWARD_LOCK) != 0) { - flags &= ~PackageManager.INSTALL_EXTERNAL; + if (fwdLocked && onSd) { + Log.w(TAG, "Cannot install fwd locked apps on sdcard"); + ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION; } else { // Remote call to find out default install location - int loc = mContainerService.getRecommendedInstallLocation(packageURI); + PackageInfoLite pkgLite = mContainerService.getMinimalPackageInfo(packageURI); + int loc = installLocationPolicy(pkgLite, flags); // Use install location to create InstallArgs and temporary // install location - if (loc == PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE){ + if (loc == PackageHelper.RECOMMEND_FAILED_INVALID_LOCATION){ + ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION; + } else if (loc == PackageHelper.RECOMMEND_FAILED_ALREADY_EXISTS){ + ret = PackageManager.INSTALL_FAILED_ALREADY_EXISTS; + } else if (loc == PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE){ ret = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; } else if (loc == PackageHelper.RECOMMEND_FAILED_INVALID_APK) { ret = PackageManager.INSTALL_FAILED_INVALID_APK; } else { + // Override install location with flags if ((flags & PackageManager.INSTALL_EXTERNAL) == 0){ if (loc == PackageHelper.RECOMMEND_INSTALL_EXTERNAL) { // Set the flag to install on external media. @@ -5934,7 +5995,7 @@ class PackageManagerService extends IPackageManager.Stub { // Delete application code and resources if (deleteCodeAndResources) { // TODO can pick up from PackageSettings as well - int installFlags = ((p.applicationInfo.flags & ApplicationInfo.FLAG_ON_SDCARD)!=0) ? + int installFlags = ((p.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE)!=0) ? PackageManager.INSTALL_EXTERNAL : 0; installFlags |= ((p.applicationInfo.flags & ApplicationInfo.FLAG_FORWARD_LOCK)!=0) ? PackageManager.INSTALL_FORWARD_LOCK : 0; @@ -7153,7 +7214,7 @@ class PackageManagerService extends IPackageManager.Stub { void setFlags(int pkgFlags) { this.pkgFlags = (pkgFlags & ApplicationInfo.FLAG_SYSTEM) | (pkgFlags & ApplicationInfo.FLAG_FORWARD_LOCK) | - (pkgFlags & ApplicationInfo.FLAG_ON_SDCARD) | + (pkgFlags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) | (pkgFlags & ApplicationInfo.FLAG_NEVER_ENCRYPT); } } @@ -8997,7 +9058,7 @@ class PackageManagerService extends IPackageManager.Stub { return false; } mMediaMounted = mediaStatus; - Set<String> appList = mSettings.findPackagesWithFlag(ApplicationInfo.FLAG_ON_SDCARD); + Set<String> appList = mSettings.findPackagesWithFlag(ApplicationInfo.FLAG_EXTERNAL_STORAGE); ret = appList != null && appList.size() > 0; } // Queue up an async operation since the package installation may take a little while. @@ -9046,7 +9107,7 @@ class PackageManagerService extends IPackageManager.Stub { if (DEBUG_SD_INSTALL) Log.i(TAG, "Looking for pkg : " + pkgName); PackageSetting ps = mSettings.mPackages.get(pkgName); if (ps != null && ps.codePathString != null && - (ps.pkgFlags & ApplicationInfo.FLAG_ON_SDCARD) != 0) { + (ps.pkgFlags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) { if (DEBUG_SD_INSTALL) Log.i(TAG, "Container : " + cid + " corresponds to pkg : " + pkgName + " at code path: " + ps.codePathString); @@ -9272,7 +9333,7 @@ class PackageManagerService extends IPackageManager.Stub { } else { newFlags = (flags & PackageManager.MOVE_EXTERNAL_MEDIA) != 0 ? PackageManager.INSTALL_EXTERNAL : 0; - currFlags = (pkg.applicationInfo.flags & ApplicationInfo.FLAG_ON_SDCARD) != 0 ? + currFlags = (pkg.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0 ? PackageManager.INSTALL_EXTERNAL : 0; if (newFlags == currFlags) { Log.w(TAG, "No move required. Trying to move to same location"); @@ -9339,9 +9400,9 @@ class PackageManagerService extends IPackageManager.Stub { ps.resourcePathString = ps.resourcePath.getPath(); // Set the application info flag correctly. if ((mp.flags & PackageManager.INSTALL_EXTERNAL) != 0) { - pkg.applicationInfo.flags |= ApplicationInfo.FLAG_ON_SDCARD; + pkg.applicationInfo.flags |= ApplicationInfo.FLAG_EXTERNAL_STORAGE; } else { - pkg.applicationInfo.flags &= ~ApplicationInfo.FLAG_ON_SDCARD; + pkg.applicationInfo.flags &= ~ApplicationInfo.FLAG_EXTERNAL_STORAGE; } ps.setFlags(pkg.applicationInfo.flags); mAppDirs.remove(oldCodePath); diff --git a/services/java/com/android/server/PowerManagerService.java b/services/java/com/android/server/PowerManagerService.java index 0189c60..fc6bfcd 100644 --- a/services/java/com/android/server/PowerManagerService.java +++ b/services/java/com/android/server/PowerManagerService.java @@ -17,6 +17,7 @@ package com.android.server; import com.android.internal.app.IBatteryStats; +import com.android.internal.app.ShutdownThread; import com.android.server.am.BatteryStatsService; import android.app.ActivityManagerNative; @@ -41,7 +42,6 @@ import android.os.Environment; import android.os.Handler; import android.os.HandlerThread; import android.os.IBinder; -import android.os.storage.IMountService; import android.os.IPowerManager; import android.os.LocalPowerManager; import android.os.Power; @@ -2202,28 +2202,35 @@ class PowerManagerService extends IPowerManager.Stub { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.REBOOT, null); - /* - * Manually shutdown the MountService to ensure media is - * put into a safe state. - */ - IMountService mSvc = IMountService.Stub.asInterface( - ServiceManager.getService("mount")); + if (mHandler == null || !ActivityManagerNative.isSystemReady()) { + throw new IllegalStateException("Too early to call reboot()"); + } + + final String finalReason = reason; + Runnable runnable = new Runnable() { + public void run() { + synchronized (this) { + ShutdownThread.reboot(mContext, finalReason, false); + // if we get here we failed + notify(); + } + + } + }; + + mHandler.post(runnable); - if (mSvc != null) { + // block until we reboot or fail. + // throw an exception if we failed to reboot + synchronized (runnable) { try { - mSvc.shutdown(); - } catch (Exception e) { - Slog.e(TAG, "MountService shutdown failed", e); + runnable.wait(); + } catch (InterruptedException e) { } - } else { - Slog.w(TAG, "MountService unavailable for shutdown"); - } - - try { - Power.reboot(reason); - } catch (IOException e) { - Slog.e(TAG, "reboot failed", e); } + + // if we get here we failed + throw new IllegalStateException("unable to reboot!"); } /** diff --git a/services/java/com/android/server/Watchdog.java b/services/java/com/android/server/Watchdog.java index f97f50a..3f64b25 100644 --- a/services/java/com/android/server/Watchdog.java +++ b/services/java/com/android/server/Watchdog.java @@ -29,6 +29,7 @@ import android.os.Debug; import android.os.Handler; import android.os.Message; import android.os.Process; +import android.os.ServiceManager; import android.os.SystemClock; import android.os.SystemProperties; import android.provider.Settings; @@ -676,11 +677,8 @@ public class Watchdog extends Thread { */ void rebootSystem(String reason) { Slog.i(TAG, "Rebooting system because: " + reason); - try { - android.os.Power.reboot(reason); - } catch (IOException e) { - Slog.e(TAG, "Reboot failed!", e); - } + PowerManagerService pms = (PowerManagerService) ServiceManager.getService("power"); + pms.reboot(reason); } /** diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java index 8fa862d..62263a6 100644 --- a/services/java/com/android/server/WifiService.java +++ b/services/java/com/android/server/WifiService.java @@ -346,9 +346,8 @@ public class WifiService extends IWifiManager.Stub { */ public boolean pingSupplicant() { enforceChangePermission(); - synchronized (mWifiStateTracker) { - return WifiNative.pingCommand(); - } + + return mWifiStateTracker.ping(); } /** @@ -357,20 +356,19 @@ public class WifiService extends IWifiManager.Stub { */ public boolean startScan(boolean forceActive) { enforceChangePermission(); - synchronized (mWifiStateTracker) { - switch (mWifiStateTracker.getSupplicantState()) { - case DISCONNECTED: - case INACTIVE: - case SCANNING: - case DORMANT: - break; - default: - WifiNative.setScanResultHandlingCommand( - WifiStateTracker.SUPPL_SCAN_HANDLING_LIST_ONLY); - break; - } - return WifiNative.scanCommand(forceActive); + + switch (mWifiStateTracker.getSupplicantState()) { + case DISCONNECTED: + case INACTIVE: + case SCANNING: + case DORMANT: + break; + default: + mWifiStateTracker.setScanResultHandling( + WifiStateTracker.SUPPL_SCAN_HANDLING_LIST_ONLY); + break; } + return mWifiStateTracker.scan(forceActive); } /** @@ -434,19 +432,18 @@ public class WifiService extends IWifiManager.Stub { } if (enable) { - synchronized (mWifiStateTracker) { - if (!WifiNative.loadDriver()) { - Slog.e(TAG, "Failed to load Wi-Fi driver."); - setWifiEnabledState(WIFI_STATE_UNKNOWN, uid); - return false; - } - if (!WifiNative.startSupplicant()) { - WifiNative.unloadDriver(); - Slog.e(TAG, "Failed to start supplicant daemon."); - setWifiEnabledState(WIFI_STATE_UNKNOWN, uid); - return false; - } + if (!mWifiStateTracker.loadDriver()) { + Slog.e(TAG, "Failed to load Wi-Fi driver."); + setWifiEnabledState(WIFI_STATE_UNKNOWN, uid); + return false; + } + if (!mWifiStateTracker.startSupplicant()) { + mWifiStateTracker.unloadDriver(); + Slog.e(TAG, "Failed to start supplicant daemon."); + setWifiEnabledState(WIFI_STATE_UNKNOWN, uid); + return false; } + registerForBroadcasts(); mWifiStateTracker.startEventLoop(); } else { @@ -456,27 +453,27 @@ public class WifiService extends IWifiManager.Stub { mWifiStateTracker.setNotificationVisible(false, 0, false, 0); boolean failedToStopSupplicantOrUnloadDriver = false; - synchronized (mWifiStateTracker) { - if (!WifiNative.stopSupplicant()) { - Slog.e(TAG, "Failed to stop supplicant daemon."); - setWifiEnabledState(WIFI_STATE_UNKNOWN, uid); - failedToStopSupplicantOrUnloadDriver = true; - } - /** - * Reset connections and disable interface - * before we unload the driver - */ - mWifiStateTracker.resetConnections(true); + if (!mWifiStateTracker.stopSupplicant()) { + Slog.e(TAG, "Failed to stop supplicant daemon."); + setWifiEnabledState(WIFI_STATE_UNKNOWN, uid); + failedToStopSupplicantOrUnloadDriver = true; + } - if (!WifiNative.unloadDriver()) { - Slog.e(TAG, "Failed to unload Wi-Fi driver."); - if (!failedToStopSupplicantOrUnloadDriver) { - setWifiEnabledState(WIFI_STATE_UNKNOWN, uid); - failedToStopSupplicantOrUnloadDriver = true; - } + /** + * Reset connections and disable interface + * before we unload the driver + */ + mWifiStateTracker.resetConnections(true); + + if (!mWifiStateTracker.unloadDriver()) { + Slog.e(TAG, "Failed to unload Wi-Fi driver."); + if (!failedToStopSupplicantOrUnloadDriver) { + setWifiEnabledState(WIFI_STATE_UNKNOWN, uid); + failedToStopSupplicantOrUnloadDriver = true; } } + if (failedToStopSupplicantOrUnloadDriver) { return false; } @@ -553,9 +550,8 @@ public class WifiService extends IWifiManager.Stub { */ public boolean disconnect() { enforceChangePermission(); - synchronized (mWifiStateTracker) { - return WifiNative.disconnectCommand(); - } + + return mWifiStateTracker.disconnect(); } /** @@ -564,9 +560,8 @@ public class WifiService extends IWifiManager.Stub { */ public boolean reconnect() { enforceChangePermission(); - synchronized (mWifiStateTracker) { - return WifiNative.reconnectCommand(); - } + + return mWifiStateTracker.reconnectCommand(); } /** @@ -575,9 +570,8 @@ public class WifiService extends IWifiManager.Stub { */ public boolean reassociate() { enforceChangePermission(); - synchronized (mWifiStateTracker) { - return WifiNative.reassociateCommand(); - } + + return mWifiStateTracker.reassociate(); } private boolean getPersistedWifiApEnabled() { @@ -650,12 +644,10 @@ public class WifiService extends IWifiManager.Stub { } if (enable) { - synchronized (mWifiStateTracker) { - if (!WifiNative.loadDriver()) { - Slog.e(TAG, "Failed to load Wi-Fi driver for AP mode"); - setWifiApEnabledState(WIFI_AP_STATE_FAILED, uid); - return false; - } + if (!mWifiStateTracker.loadDriver()) { + Slog.e(TAG, "Failed to load Wi-Fi driver for AP mode"); + setWifiApEnabledState(WIFI_AP_STATE_FAILED, uid); + return false; } try { @@ -672,12 +664,10 @@ public class WifiService extends IWifiManager.Stub { Slog.e(TAG, "Exception in stopAccessPoint()"); } - synchronized (mWifiStateTracker) { - if (!WifiNative.unloadDriver()) { - Slog.e(TAG, "Failed to unload Wi-Fi driver for AP mode"); - setWifiApEnabledState(WIFI_AP_STATE_FAILED, uid); - return false; - } + if (!mWifiStateTracker.unloadDriver()) { + Slog.e(TAG, "Failed to unload Wi-Fi driver for AP mode"); + setWifiApEnabledState(WIFI_AP_STATE_FAILED, uid); + return false; } } @@ -735,15 +725,15 @@ public class WifiService extends IWifiManager.Stub { public List<WifiConfiguration> getConfiguredNetworks() { enforceAccessPermission(); String listStr; + /* * We don't cache the list, because we want to allow * for the possibility that the configuration file * has been modified through some external means, * such as the wpa_cli command line program. */ - synchronized (mWifiStateTracker) { - listStr = WifiNative.listNetworksCommand(); - } + listStr = mWifiStateTracker.listNetworks(); + List<WifiConfiguration> networks = new ArrayList<WifiConfiguration>(); if (listStr == null) @@ -767,11 +757,10 @@ public class WifiService extends IWifiManager.Stub { config.status = WifiConfiguration.Status.DISABLED; else config.status = WifiConfiguration.Status.ENABLED; - } else + } else { config.status = WifiConfiguration.Status.ENABLED; - synchronized (mWifiStateTracker) { - readNetworkVariables(config); } + readNetworkVariables(config); networks.add(config); } @@ -785,7 +774,7 @@ public class WifiService extends IWifiManager.Stub { * The caller must hold the synchronization monitor. * @param config the {@link WifiConfiguration} object to be filled in. */ - private static void readNetworkVariables(WifiConfiguration config) { + private void readNetworkVariables(WifiConfiguration config) { int netId = config.networkId; if (netId < 0) @@ -798,21 +787,21 @@ public class WifiService extends IWifiManager.Stub { */ String value; - value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.ssidVarName); + value = mWifiStateTracker.getNetworkVariable(netId, WifiConfiguration.ssidVarName); if (!TextUtils.isEmpty(value)) { config.SSID = removeDoubleQuotes(value); } else { config.SSID = null; } - value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.bssidVarName); + value = mWifiStateTracker.getNetworkVariable(netId, WifiConfiguration.bssidVarName); if (!TextUtils.isEmpty(value)) { config.BSSID = value; } else { config.BSSID = null; } - value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.priorityVarName); + value = mWifiStateTracker.getNetworkVariable(netId, WifiConfiguration.priorityVarName); config.priority = -1; if (!TextUtils.isEmpty(value)) { try { @@ -821,7 +810,7 @@ public class WifiService extends IWifiManager.Stub { } } - value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.hiddenSSIDVarName); + value = mWifiStateTracker.getNetworkVariable(netId, WifiConfiguration.hiddenSSIDVarName); config.hiddenSSID = false; if (!TextUtils.isEmpty(value)) { try { @@ -830,7 +819,7 @@ public class WifiService extends IWifiManager.Stub { } } - value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.wepTxKeyIdxVarName); + value = mWifiStateTracker.getNetworkVariable(netId, WifiConfiguration.wepTxKeyIdxVarName); config.wepTxKeyIndex = -1; if (!TextUtils.isEmpty(value)) { try { @@ -844,7 +833,7 @@ public class WifiService extends IWifiManager.Stub { * just a "*" if the key is set, or the null string otherwise. */ for (int i = 0; i < 4; i++) { - value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.wepKeyVarNames[i]); + value = mWifiStateTracker.getNetworkVariable(netId, WifiConfiguration.wepKeyVarNames[i]); if (!TextUtils.isEmpty(value)) { config.wepKeys[i] = value; } else { @@ -856,14 +845,14 @@ public class WifiService extends IWifiManager.Stub { * Get the private shared key. Note that the actual keys are not passed back, * just a "*" if the key is set, or the null string otherwise. */ - value = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.pskVarName); + value = mWifiStateTracker.getNetworkVariable(netId, WifiConfiguration.pskVarName); if (!TextUtils.isEmpty(value)) { config.preSharedKey = value; } else { config.preSharedKey = null; } - value = WifiNative.getNetworkVariableCommand(config.networkId, + value = mWifiStateTracker.getNetworkVariable(config.networkId, WifiConfiguration.Protocol.varName); if (!TextUtils.isEmpty(value)) { String vals[] = value.split(" "); @@ -876,7 +865,7 @@ public class WifiService extends IWifiManager.Stub { } } - value = WifiNative.getNetworkVariableCommand(config.networkId, + value = mWifiStateTracker.getNetworkVariable(config.networkId, WifiConfiguration.KeyMgmt.varName); if (!TextUtils.isEmpty(value)) { String vals[] = value.split(" "); @@ -889,7 +878,7 @@ public class WifiService extends IWifiManager.Stub { } } - value = WifiNative.getNetworkVariableCommand(config.networkId, + value = mWifiStateTracker.getNetworkVariable(config.networkId, WifiConfiguration.AuthAlgorithm.varName); if (!TextUtils.isEmpty(value)) { String vals[] = value.split(" "); @@ -902,7 +891,7 @@ public class WifiService extends IWifiManager.Stub { } } - value = WifiNative.getNetworkVariableCommand(config.networkId, + value = mWifiStateTracker.getNetworkVariable(config.networkId, WifiConfiguration.PairwiseCipher.varName); if (!TextUtils.isEmpty(value)) { String vals[] = value.split(" "); @@ -915,7 +904,7 @@ public class WifiService extends IWifiManager.Stub { } } - value = WifiNative.getNetworkVariableCommand(config.networkId, + value = mWifiStateTracker.getNetworkVariable(config.networkId, WifiConfiguration.GroupCipher.varName); if (!TextUtils.isEmpty(value)) { String vals[] = value.split(" "); @@ -930,7 +919,7 @@ public class WifiService extends IWifiManager.Stub { for (WifiConfiguration.EnterpriseField field : config.enterpriseFields) { - value = WifiNative.getNetworkVariableCommand(netId, + value = mWifiStateTracker.getNetworkVariable(netId, field.varName()); if (!TextUtils.isEmpty(value)) { if (field != config.eap) value = removeDoubleQuotes(value); @@ -955,6 +944,7 @@ public class WifiService extends IWifiManager.Stub { */ public int addOrUpdateNetwork(WifiConfiguration config) { enforceChangePermission(); + /* * If the supplied networkId is -1, we create a new empty * network configuration. Otherwise, the networkId should @@ -967,7 +957,7 @@ public class WifiService extends IWifiManager.Stub { // networkId of -1 means we want to create a new network synchronized (mWifiStateTracker) { if (newNetwork) { - netId = WifiNative.addNetworkCommand(); + netId = mWifiStateTracker.addNetwork(); if (netId < 0) { if (DBG) { Slog.d(TAG, "Failed to add a network!"); @@ -976,7 +966,8 @@ public class WifiService extends IWifiManager.Stub { } doReconfig = true; } else { - String priorityVal = WifiNative.getNetworkVariableCommand(netId, WifiConfiguration.priorityVarName); + String priorityVal = mWifiStateTracker.getNetworkVariable( + netId, WifiConfiguration.priorityVarName); currentPriority = -1; if (!TextUtils.isEmpty(priorityVal)) { try { @@ -987,16 +978,17 @@ public class WifiService extends IWifiManager.Stub { doReconfig = currentPriority != config.priority; } mNeedReconfig = mNeedReconfig || doReconfig; + } - setVariables: { + setVariables: { /* * Note that if a networkId for a non-existent network - * was supplied, then the first setNetworkVariableCommand() + * was supplied, then the first setNetworkVariable() * will fail, so we don't bother to make a separate check * for the validity of the ID up front. */ if (config.SSID != null && - !WifiNative.setNetworkVariableCommand( + !mWifiStateTracker.setNetworkVariable( netId, WifiConfiguration.ssidVarName, convertToQuotedString(config.SSID))) { @@ -1007,7 +999,7 @@ public class WifiService extends IWifiManager.Stub { } if (config.BSSID != null && - !WifiNative.setNetworkVariableCommand( + !mWifiStateTracker.setNetworkVariable( netId, WifiConfiguration.bssidVarName, config.BSSID)) { @@ -1020,7 +1012,7 @@ public class WifiService extends IWifiManager.Stub { String allowedKeyManagementString = makeString(config.allowedKeyManagement, WifiConfiguration.KeyMgmt.strings); if (config.allowedKeyManagement.cardinality() != 0 && - !WifiNative.setNetworkVariableCommand( + !mWifiStateTracker.setNetworkVariable( netId, WifiConfiguration.KeyMgmt.varName, allowedKeyManagementString)) { @@ -1034,7 +1026,7 @@ public class WifiService extends IWifiManager.Stub { String allowedProtocolsString = makeString(config.allowedProtocols, WifiConfiguration.Protocol.strings); if (config.allowedProtocols.cardinality() != 0 && - !WifiNative.setNetworkVariableCommand( + !mWifiStateTracker.setNetworkVariable( netId, WifiConfiguration.Protocol.varName, allowedProtocolsString)) { @@ -1048,7 +1040,7 @@ public class WifiService extends IWifiManager.Stub { String allowedAuthAlgorithmsString = makeString(config.allowedAuthAlgorithms, WifiConfiguration.AuthAlgorithm.strings); if (config.allowedAuthAlgorithms.cardinality() != 0 && - !WifiNative.setNetworkVariableCommand( + !mWifiStateTracker.setNetworkVariable( netId, WifiConfiguration.AuthAlgorithm.varName, allowedAuthAlgorithmsString)) { @@ -1062,7 +1054,7 @@ public class WifiService extends IWifiManager.Stub { String allowedPairwiseCiphersString = makeString(config.allowedPairwiseCiphers, WifiConfiguration.PairwiseCipher.strings); if (config.allowedPairwiseCiphers.cardinality() != 0 && - !WifiNative.setNetworkVariableCommand( + !mWifiStateTracker.setNetworkVariable( netId, WifiConfiguration.PairwiseCipher.varName, allowedPairwiseCiphersString)) { @@ -1076,7 +1068,7 @@ public class WifiService extends IWifiManager.Stub { String allowedGroupCiphersString = makeString(config.allowedGroupCiphers, WifiConfiguration.GroupCipher.strings); if (config.allowedGroupCiphers.cardinality() != 0 && - !WifiNative.setNetworkVariableCommand( + !mWifiStateTracker.setNetworkVariable( netId, WifiConfiguration.GroupCipher.varName, allowedGroupCiphersString)) { @@ -1090,7 +1082,7 @@ public class WifiService extends IWifiManager.Stub { // Prevent client screw-up by passing in a WifiConfiguration we gave it // by preventing "*" as a key. if (config.preSharedKey != null && !config.preSharedKey.equals("*") && - !WifiNative.setNetworkVariableCommand( + !mWifiStateTracker.setNetworkVariable( netId, WifiConfiguration.pskVarName, config.preSharedKey)) { @@ -1106,7 +1098,7 @@ public class WifiService extends IWifiManager.Stub { // Prevent client screw-up by passing in a WifiConfiguration we gave it // by preventing "*" as a key. if (config.wepKeys[i] != null && !config.wepKeys[i].equals("*")) { - if (!WifiNative.setNetworkVariableCommand( + if (!mWifiStateTracker.setNetworkVariable( netId, WifiConfiguration.wepKeyVarNames[i], config.wepKeys[i])) { @@ -1123,7 +1115,7 @@ public class WifiService extends IWifiManager.Stub { } if (hasSetKey) { - if (!WifiNative.setNetworkVariableCommand( + if (!mWifiStateTracker.setNetworkVariable( netId, WifiConfiguration.wepTxKeyIdxVarName, Integer.toString(config.wepTxKeyIndex))) { @@ -1136,7 +1128,7 @@ public class WifiService extends IWifiManager.Stub { } } - if (!WifiNative.setNetworkVariableCommand( + if (!mWifiStateTracker.setNetworkVariable( netId, WifiConfiguration.priorityVarName, Integer.toString(config.priority))) { @@ -1147,7 +1139,7 @@ public class WifiService extends IWifiManager.Stub { break setVariables; } - if (config.hiddenSSID && !WifiNative.setNetworkVariableCommand( + if (config.hiddenSSID && !mWifiStateTracker.setNetworkVariable( netId, WifiConfiguration.hiddenSSIDVarName, Integer.toString(config.hiddenSSID ? 1 : 0))) { @@ -1166,7 +1158,7 @@ public class WifiService extends IWifiManager.Stub { if (field != config.eap) { value = (value.length() == 0) ? "NULL" : convertToQuotedString(value); } - if (!WifiNative.setNetworkVariableCommand( + if (!mWifiStateTracker.setNetworkVariable( netId, varName, value)) { @@ -1179,21 +1171,20 @@ public class WifiService extends IWifiManager.Stub { } } return netId; - } + } - /* - * For an update, if one of the setNetworkVariable operations fails, - * we might want to roll back all the changes already made. But the - * chances are that if anything is going to go wrong, it'll happen - * the first time we try to set one of the variables. - */ - if (newNetwork) { - removeNetwork(netId); - if (DBG) { - Slog.d(TAG, - "Failed to set a network variable, removed network: " - + netId); - } + /* + * For an update, if one of the setNetworkVariable operations fails, + * we might want to roll back all the changes already made. But the + * chances are that if anything is going to go wrong, it'll happen + * the first time we try to set one of the variables. + */ + if (newNetwork) { + removeNetwork(netId); + if (DBG) { + Slog.d(TAG, + "Failed to set a network variable, removed network: " + + netId); } } return -1; @@ -1260,15 +1251,13 @@ public class WifiService extends IWifiManager.Stub { public boolean enableNetwork(int netId, boolean disableOthers) { enforceChangePermission(); - synchronized (mWifiStateTracker) { - String ifname = mWifiStateTracker.getInterfaceName(); - NetworkUtils.enableInterface(ifname); - boolean result = WifiNative.enableNetworkCommand(netId, disableOthers); - if (!result) { - NetworkUtils.disableInterface(ifname); - } - return result; + String ifname = mWifiStateTracker.getInterfaceName(); + NetworkUtils.enableInterface(ifname); + boolean result = mWifiStateTracker.enableNetwork(netId, disableOthers); + if (!result) { + NetworkUtils.disableInterface(ifname); } + return result; } /** @@ -1280,9 +1269,7 @@ public class WifiService extends IWifiManager.Stub { public boolean disableNetwork(int netId) { enforceChangePermission(); - synchronized (mWifiStateTracker) { - return WifiNative.disableNetworkCommand(netId); - } + return mWifiStateTracker.disableNetwork(netId); } /** @@ -1306,9 +1293,8 @@ public class WifiService extends IWifiManager.Stub { public List<ScanResult> getScanResults() { enforceAccessPermission(); String reply; - synchronized (mWifiStateTracker) { - reply = WifiNative.scanResultsCommand(); - } + + reply = mWifiStateTracker.scanResults(); if (reply == null) { return null; } @@ -1456,11 +1442,12 @@ public class WifiService extends IWifiManager.Stub { public boolean saveConfiguration() { boolean result; enforceChangePermission(); + synchronized (mWifiStateTracker) { - result = WifiNative.saveConfigCommand(); + result = mWifiStateTracker.saveConfig(); if (result && mNeedReconfig) { mNeedReconfig = false; - result = WifiNative.reloadConfigCommand(); + result = mWifiStateTracker.reloadConfig(); if (result) { Intent intent = new Intent(WifiManager.NETWORK_IDS_CHANGED_ACTION); @@ -1532,18 +1519,17 @@ public class WifiService extends IWifiManager.Stub { int numChannels; enforceAccessPermission(); - synchronized (mWifiStateTracker) { - /* - * If we can't get the value from the driver (e.g., because - * Wi-Fi is not currently enabled), get the value from - * Settings. - */ - numChannels = WifiNative.getNumAllowedChannelsCommand(); - if (numChannels < 0) { - numChannels = Settings.Secure.getInt(mContext.getContentResolver(), - Settings.Secure.WIFI_NUM_ALLOWED_CHANNELS, - -1); - } + + /* + * If we can't get the value from the driver (e.g., because + * Wi-Fi is not currently enabled), get the value from + * Settings. + */ + numChannels = mWifiStateTracker.getNumAllowedChannels(); + if (numChannels < 0) { + numChannels = Settings.Secure.getInt(mContext.getContentResolver(), + Settings.Secure.WIFI_NUM_ALLOWED_CHANNELS, + -1); } return numChannels; } @@ -2126,14 +2112,13 @@ public class WifiService extends IWifiManager.Stub { public void initializeMulticastFiltering() { enforceMulticastChangePermission(); + synchronized (mMulticasters) { // if anybody had requested filters be off, leave off if (mMulticasters.size() != 0) { return; } else { - synchronized (mWifiStateTracker) { - WifiNative.startPacketFiltering(); - } + mWifiStateTracker.startPacketFiltering(); } } } @@ -2148,9 +2133,7 @@ public class WifiService extends IWifiManager.Stub { // our new size == 1 (first call), but this function won't // be called often and by making the stopPacket call each // time we're less fragile and self-healing. - synchronized (mWifiStateTracker) { - WifiNative.stopPacketFiltering(); - } + mWifiStateTracker.stopPacketFiltering(); } int uid = Binder.getCallingUid(); @@ -2182,13 +2165,12 @@ public class WifiService extends IWifiManager.Stub { private void removeMulticasterLocked(int i, int uid) { Multicaster removed = mMulticasters.remove(i); + if (removed != null) { removed.unlinkDeathRecipient(); } if (mMulticasters.size() == 0) { - synchronized (mWifiStateTracker) { - WifiNative.startPacketFiltering(); - } + mWifiStateTracker.startPacketFiltering(); } Long ident = Binder.clearCallingIdentity(); diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java index f5d3e8e..34a7fc0 100644 --- a/services/java/com/android/server/WindowManagerService.java +++ b/services/java/com/android/server/WindowManagerService.java @@ -10267,9 +10267,10 @@ public class WindowManagerService extends IWindowManager.Stub && buttonBrightness < 0) { buttonBrightness = w.mAttrs.buttonBrightness; } - if (attrs.type == WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG - || attrs.type == WindowManager.LayoutParams.TYPE_KEYGUARD - || attrs.type == WindowManager.LayoutParams.TYPE_SYSTEM_ERROR) { + if (canBeSeen + && (attrs.type == WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG + || attrs.type == WindowManager.LayoutParams.TYPE_KEYGUARD + || attrs.type == WindowManager.LayoutParams.TYPE_SYSTEM_ERROR)) { syswin = true; } } diff --git a/services/java/com/android/server/connectivity/Tethering.java b/services/java/com/android/server/connectivity/Tethering.java index 78329db..e52cd47 100644 --- a/services/java/com/android/server/connectivity/Tethering.java +++ b/services/java/com/android/server/connectivity/Tethering.java @@ -54,14 +54,12 @@ import java.util.Set; * @hide * * Timeout - * TODO - review error states - they currently are dead-ends with no recovery possible * * TODO - look for parent classes and code sharing */ public class Tethering extends INetworkManagementEventObserver.Stub { - private Notification mTetheringNotification; private Context mContext; private final String TAG = "Tethering"; @@ -92,7 +90,9 @@ public class Tethering extends INetworkManagementEventObserver.Stub { private boolean mUseHiPri; private String mUpstreamIfaceName; - HierarchicalStateMachine mTetherMasterSM; + private HierarchicalStateMachine mTetherMasterSM; + + private Notification mTetheredNotification; public Tethering(Context context) { Log.d(TAG, "Tethering starting"); @@ -152,18 +152,11 @@ public class Tethering extends INetworkManagementEventObserver.Stub { Log.d(TAG, "interfaceLinkStatusChanged " + iface + ", " + link); boolean found = false; boolean usb = false; - for (String regex : mTetherableWifiRegexs) { - if (iface.matches(regex)) { - found = true; - break; - } - } - for (String regex: mTetherableUsbRegexs) { - if (iface.matches(regex)) { - found = true; - usb = true; - break; - } + if (isWifi(iface)) { + found = true; + } else if (isUsb(iface)) { + found = true; + usb = true; } if (found == false) return; @@ -184,23 +177,31 @@ public class Tethering extends INetworkManagementEventObserver.Stub { } } + private boolean isUsb(String iface) { + for (String regex : mTetherableUsbRegexs) { + if (iface.matches(regex)) return true; + } + return false; + } + + public boolean isWifi(String iface) { + for (String regex : mTetherableWifiRegexs) { + if (iface.matches(regex)) return true; + } + return false; + } + public void interfaceAdded(String iface) { IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE); INetworkManagementService service = INetworkManagementService.Stub.asInterface(b); boolean found = false; boolean usb = false; - for (String regex : mTetherableWifiRegexs) { - if (iface.matches(regex)) { - found = true; - break; - } + if (isWifi(iface)) { + found = true; } - for (String regex : mTetherableUsbRegexs) { - if (iface.matches(regex)) { - found = true; - usb = true; - break; - } + if (isUsb(iface)) { + found = true; + usb = true; } if (found == false) { Log.d(TAG, iface + " is not a tetherable iface, ignoring"); @@ -293,6 +294,9 @@ public class Tethering extends INetworkManagementEventObserver.Stub { ArrayList<String> activeList = new ArrayList<String>(); ArrayList<String> erroredList = new ArrayList<String>(); + boolean wifiTethered = false; + boolean usbTethered = false; + synchronized (mIfaces) { Set ifaces = mIfaces.keySet(); for (Object iface : ifaces) { @@ -303,6 +307,11 @@ public class Tethering extends INetworkManagementEventObserver.Stub { } else if (sm.isAvailable()) { availableList.add((String)iface); } else if (sm.isTethered()) { + if (isUsb((String)iface)) { + usbTethered = true; + } else if (isWifi((String)iface)) { + wifiTethered = true; + } activeList.add((String)iface); } } @@ -318,6 +327,58 @@ public class Tethering extends INetworkManagementEventObserver.Stub { mContext.sendStickyBroadcast(broadcast); Log.d(TAG, "sendTetherStateChangedBroadcast " + availableList.size() + ", " + activeList.size() + ", " + erroredList.size()); + + if (usbTethered) { + if (wifiTethered) { + showTetheredNotification(com.android.internal.R.drawable.stat_sys_tether_general); + } else { + showTetheredNotification(com.android.internal.R.drawable.stat_sys_tether_usb); + } + } else if (wifiTethered) { + showTetheredNotification(com.android.internal.R.drawable.stat_sys_tether_wifi); + } else { + clearTetheredNotification(); + } + } + + private void showTetheredNotification(int icon) { + NotificationManager notificationManager = + (NotificationManager)mContext.getSystemService(Context.NOTIFICATION_SERVICE); + if (notificationManager == null) { + return; + } + + Intent intent = new Intent(); + intent.setClassName("com.android.settings", "com.android.settings.TetherSettings"); + intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY); + + PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0); + + Resources r = Resources.getSystem(); + CharSequence title = r.getText(com.android.internal.R.string.tethered_notification_title); + CharSequence message = r.getText(com.android.internal.R.string. + tethered_notification_message); + + if(mTetheredNotification == null) { + mTetheredNotification = new Notification(); + mTetheredNotification.when = 0; + } + mTetheredNotification.icon = icon; + mTetheredNotification.defaults &= ~Notification.DEFAULT_SOUND; + mTetheredNotification.flags = Notification.FLAG_ONGOING_EVENT; + mTetheredNotification.tickerText = title; + mTetheredNotification.setLatestEventInfo(mContext, title, message, pi); + + notificationManager.notify(mTetheredNotification.icon, mTetheredNotification); + } + + private void clearTetheredNotification() { + NotificationManager notificationManager = + (NotificationManager)mContext.getSystemService(Context.NOTIFICATION_SERVICE); + if (notificationManager != null && mTetheredNotification != null) { + notificationManager.cancel(mTetheredNotification.icon); + mTetheredNotification = null; + } } private class StateReceiver extends BroadcastReceiver { @@ -367,13 +428,11 @@ public class Tethering extends INetworkManagementEventObserver.Stub { return; } for (String iface : ifaces) { - for (String regex : mTetherableUsbRegexs) { - if (iface.matches(regex)) { - if (enable) { - interfaceAdded(iface); - } else { - interfaceRemoved(iface); - } + if (isUsb(iface)) { + if (enable) { + interfaceAdded(iface); + } else { + interfaceRemoved(iface); } } } @@ -420,27 +479,25 @@ public class Tethering extends INetworkManagementEventObserver.Stub { return false; } for (String iface : ifaces) { - for (String regex : mTetherableUsbRegexs) { - if (iface.matches(regex)) { - InterfaceConfiguration ifcg = null; - try { - ifcg = service.getInterfaceConfig(iface); - if (ifcg != null) { - ifcg.ipAddr = (169 << 24) + (254 << 16) + (2 << 8) + 1; - ifcg.netmask = (255 << 24) + (255 << 16) + (255 << 8) + 0; - if (enabled) { - ifcg.interfaceFlags = ifcg.interfaceFlags.replace("down", "up"); - } else { - ifcg.interfaceFlags = ifcg.interfaceFlags.replace("up", "down"); - } - ifcg.interfaceFlags = ifcg.interfaceFlags.replace("running", ""); - ifcg.interfaceFlags = ifcg.interfaceFlags.replace(" "," "); - service.setInterfaceConfig(iface, ifcg); + if (isUsb(iface)) { + InterfaceConfiguration ifcg = null; + try { + ifcg = service.getInterfaceConfig(iface); + if (ifcg != null) { + ifcg.ipAddr = (169 << 24) + (254 << 16) + (2 << 8) + 1; + ifcg.netmask = (255 << 24) + (255 << 16) + (255 << 8) + 0; + if (enabled) { + ifcg.interfaceFlags = ifcg.interfaceFlags.replace("down", "up"); + } else { + ifcg.interfaceFlags = ifcg.interfaceFlags.replace("up", "down"); } - } catch (Exception e) { - Log.e(TAG, "Error configuring interface " + iface + ", :" + e); - return false; + ifcg.interfaceFlags = ifcg.interfaceFlags.replace("running", ""); + ifcg.interfaceFlags = ifcg.interfaceFlags.replace(" "," "); + service.setInterfaceConfig(iface, ifcg); } + } catch (Exception e) { + Log.e(TAG, "Error configuring interface " + iface + ", :" + e); + return false; } } } diff --git a/tests/AndroidTests/AndroidManifest.xml b/tests/AndroidTests/AndroidManifest.xml index e06c3a8..1f0bf4d 100644 --- a/tests/AndroidTests/AndroidManifest.xml +++ b/tests/AndroidTests/AndroidManifest.xml @@ -35,6 +35,7 @@ <uses-permission android:name="android.permission.ASEC_DESTROY" /> <uses-permission android:name="android.permission.ASEC_MOUNT_UNMOUNT" /> <uses-permission android:name="android.permission.ASEC_RENAME" /> + <uses-permission android:name="android.permission.SHUTDOWN" /> <uses-permission android:name="com.android.unit_tests.permission.TEST_GRANTED" /> <uses-permission android:name="com.google.android.googleapps.permission.ACCESS_GOOGLE_PASSWORD" /> <uses-permission android:name="com.google.android.googleapps.permission.GOOGLE_AUTH" /> diff --git a/tests/AndroidTests/src/com/android/unit_tests/AsecTests.java b/tests/AndroidTests/src/com/android/unit_tests/AsecTests.java index c7bacd4..9aed363 100755 --- a/tests/AndroidTests/src/com/android/unit_tests/AsecTests.java +++ b/tests/AndroidTests/src/com/android/unit_tests/AsecTests.java @@ -16,6 +16,8 @@ package com.android.unit_tests; +import com.android.unit_tests.PackageManagerTests.StorageListener; + import android.os.storage.IMountService.Stub; import android.net.Uri; @@ -41,6 +43,9 @@ import android.os.Environment; import android.os.Handler; import android.os.IBinder; import android.os.storage.IMountService; +import android.os.storage.IMountShutdownObserver; +import android.os.storage.StorageEventListener; +import android.os.storage.StorageManager; import android.os.storage.StorageResultCode; import android.os.RemoteException; import android.os.ServiceManager; @@ -365,4 +370,256 @@ public class AsecTests extends AndroidTestCase { } } + /*------------ Tests for unmounting volume ---*/ + public final long MAX_WAIT_TIME=120*1000; + public final long WAIT_TIME_INCR=20*1000; + boolean getMediaState() { + try { + String mPath = Environment.getExternalStorageDirectory().toString(); + String state = getMs().getVolumeState(mPath); + return Environment.MEDIA_MOUNTED.equals(state); + } catch (RemoteException e) { + return false; + } + } + + boolean mountMedia() { + if (getMediaState()) { + return true; + } + try { + String mPath = Environment.getExternalStorageDirectory().toString(); + int ret = getMs().mountVolume(mPath); + return ret == StorageResultCode.OperationSucceeded; + } catch (RemoteException e) { + return false; + } + } + + class StorageListener extends StorageEventListener { + String oldState; + String newState; + String path; + private boolean doneFlag = false; + + public void action() { + synchronized (this) { + doneFlag = true; + notifyAll(); + } + } + + public boolean isDone() { + return doneFlag; + } + + @Override + public void onStorageStateChanged(String path, String oldState, String newState) { + if (localLOGV) Log.i(TAG, "Storage state changed from " + oldState + " to " + newState); + this.oldState = oldState; + this.newState = newState; + this.path = path; + action(); + } + } + + private boolean unmountMedia() { + if (!getMediaState()) { + return true; + } + String path = Environment.getExternalStorageDirectory().toString(); + StorageListener observer = new StorageListener(); + StorageManager sm = (StorageManager) mContext.getSystemService(Context.STORAGE_SERVICE); + sm.registerListener(observer); + try { + // Wait on observer + synchronized(observer) { + getMs().unmountVolume(path, false); + long waitTime = 0; + while((!observer.isDone()) && (waitTime < MAX_WAIT_TIME) ) { + observer.wait(WAIT_TIME_INCR); + waitTime += WAIT_TIME_INCR; + } + if(!observer.isDone()) { + throw new Exception("Timed out waiting for packageInstalled callback"); + } + return true; + } + } catch (Exception e) { + return false; + } finally { + sm.unregisterListener(observer); + } + } + public void testUnmount() { + boolean oldStatus = getMediaState(); + Log.i(TAG, "oldStatus="+oldStatus); + try { + // Mount media firsts + if (!getMediaState()) { + mountMedia(); + } + assertTrue(unmountMedia()); + } finally { + // Restore old status + boolean currStatus = getMediaState(); + if (oldStatus != currStatus) { + if (oldStatus) { + // Mount media + mountMedia(); + } else { + unmountMedia(); + } + } + } + } + + class MultipleStorageLis extends StorageListener { + int count = 0; + public void onStorageStateChanged(String path, String oldState, String newState) { + count++; + super.action(); + } + } + /* + * This test invokes unmount multiple time and expects the call back + * to be invoked just once. + */ + public void testUnmountMultiple() { + boolean oldStatus = getMediaState(); + StorageManager sm = (StorageManager) mContext.getSystemService(Context.STORAGE_SERVICE); + MultipleStorageLis observer = new MultipleStorageLis(); + try { + // Mount media firsts + if (!getMediaState()) { + mountMedia(); + } + String path = Environment.getExternalStorageDirectory().toString(); + sm.registerListener(observer); + // Wait on observer + synchronized(observer) { + for (int i = 0; i < 5; i++) { + getMs().unmountVolume(path, false); + } + long waitTime = 0; + while((!observer.isDone()) && (waitTime < MAX_WAIT_TIME) ) { + observer.wait(WAIT_TIME_INCR); + waitTime += WAIT_TIME_INCR; + } + if(!observer.isDone()) { + failStr("Timed out waiting for packageInstalled callback"); + } + } + assertEquals(observer.count, 1); + } catch (Exception e) { + failStr(e); + } finally { + sm.unregisterListener(observer); + // Restore old status + boolean currStatus = getMediaState(); + if (oldStatus != currStatus) { + if (oldStatus) { + // Mount media + mountMedia(); + } else { + unmountMedia(); + } + } + } + } + + class ShutdownObserver extends IMountShutdownObserver.Stub{ + private boolean doneFlag = false; + int statusCode; + + public void action() { + synchronized (this) { + doneFlag = true; + notifyAll(); + } + } + + public boolean isDone() { + return doneFlag; + } + public void onShutDownComplete(int statusCode) throws RemoteException { + this.statusCode = statusCode; + action(); + } + + } + + boolean invokeShutdown() { + IMountService ms = getMs(); + ShutdownObserver observer = new ShutdownObserver(); + synchronized (observer) { + try { + ms.shutdown(observer); + return true; + } catch (RemoteException e) { + failStr(e); + } + } + return false; + } + + public void testShutdown() { + boolean oldStatus = getMediaState(); + try { + // Mount media firsts + if (!getMediaState()) { + mountMedia(); + } + assertTrue(invokeShutdown()); + } finally { + // Restore old status + boolean currStatus = getMediaState(); + if (oldStatus != currStatus) { + if (oldStatus) { + // Mount media + mountMedia(); + } else { + unmountMedia(); + } + } + } + } + + /* + * This test invokes unmount multiple time and expects the call back + * to be invoked just once. + */ + public void testShutdownMultiple() { + boolean oldStatus = getMediaState(); + try { + // Mount media firsts + if (!getMediaState()) { + mountMedia(); + } + IMountService ms = getMs(); + ShutdownObserver observer = new ShutdownObserver(); + synchronized (observer) { + try { + ms.shutdown(observer); + for (int i = 0; i < 4; i++) { + ms.shutdown(null); + } + } catch (RemoteException e) { + failStr(e); + } + } + } finally { + // Restore old status + boolean currStatus = getMediaState(); + if (oldStatus != currStatus) { + if (oldStatus) { + // Mount media + mountMedia(); + } else { + unmountMedia(); + } + } + } + } + } diff --git a/tests/AndroidTests/src/com/android/unit_tests/PackageManagerTests.java b/tests/AndroidTests/src/com/android/unit_tests/PackageManagerTests.java index 50eca02..d4435d3 100755 --- a/tests/AndroidTests/src/com/android/unit_tests/PackageManagerTests.java +++ b/tests/AndroidTests/src/com/android/unit_tests/PackageManagerTests.java @@ -315,9 +315,9 @@ public class PackageManagerTests extends AndroidTestCase { if (!getInstallLoc(flags, expInstallLocation)) { assertEquals(srcPath, appInstallPath); assertEquals(publicSrcPath, appInstallPath); - assertFalse((info.flags & ApplicationInfo.FLAG_ON_SDCARD) != 0); + assertFalse((info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0); } else { - assertTrue((info.flags & ApplicationInfo.FLAG_ON_SDCARD) != 0); + assertTrue((info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0); assertTrue(srcPath.startsWith(SECURE_CONTAINERS_PREFIX)); assertTrue(publicSrcPath.startsWith(SECURE_CONTAINERS_PREFIX)); } @@ -932,11 +932,86 @@ public class PackageManagerTests extends AndroidTestCase { 0, true, false, -1, PackageInfo.INSTALL_LOCATION_AUTO); } + public void testManifestInstallLocationFwdLockedFlagSdcard() { + installFromRawResource("install.apk", R.raw.install_loc_unspecified, + PackageManager.INSTALL_FORWARD_LOCK | + PackageManager.INSTALL_EXTERNAL, true, true, + PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION, + PackageInfo.INSTALL_LOCATION_AUTO); + } + public void testManifestInstallLocationFwdLockedSdcard() { installFromRawResource("install.apk", R.raw.install_loc_sdcard, PackageManager.INSTALL_FORWARD_LOCK, true, false, -1, - PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY); + PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL); + } + + private void replaceManifestLocation(int iFlags, int rFlags) { + InstallParams ip = sampleInstallFromRawResource(iFlags, false); + GenericReceiver receiver = new ReplaceReceiver(ip.pkg.packageName); + int replaceFlags = rFlags | PackageManager.INSTALL_REPLACE_EXISTING; + try { + assertEquals(invokeInstallPackage(ip.packageURI, replaceFlags, + ip.pkg.packageName, receiver), true); + assertInstall(ip.pkg, replaceFlags, ip.pkg.installLocation); + } catch (Exception e) { + failStr("Failed with exception : " + e); + } finally { + cleanUpInstall(ip); + } + } + + public void testReplaceFlagInternalSdcard() { + replaceManifestLocation(0, PackageManager.INSTALL_EXTERNAL); + } + + public void testReplaceFlagSdcardInternal() { + replaceManifestLocation(PackageManager.INSTALL_EXTERNAL, 0); + } + + public void testManifestInstallLocationReplaceInternalSdcard() { + int iFlags = 0; + int iApk = R.raw.install_loc_unspecified; + int rFlags = 0; + int rApk = R.raw.install_loc_sdcard; + InstallParams ip = installFromRawResource("install.apk", iApk, + iFlags, false, + false, -1, PackageInfo.INSTALL_LOCATION_AUTO); + GenericReceiver receiver = new ReplaceReceiver(ip.pkg.packageName); + int replaceFlags = rFlags | PackageManager.INSTALL_REPLACE_EXISTING; + try { + InstallParams rp = installFromRawResource("install.apk", rApk, + rFlags, false, + false, -1, PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL); + assertInstall(rp.pkg, replaceFlags, rp.pkg.installLocation); + } catch (Exception e) { + failStr("Failed with exception : " + e); + } finally { + cleanUpInstall(ip); + } + } + + public void testManifestInstallLocationReplaceSdcardInternal() { + int iFlags = 0; + int iApk = R.raw.install_loc_sdcard; + int rFlags = 0; + int rApk = R.raw.install_loc_unspecified; + InstallParams ip = installFromRawResource("install.apk", iApk, + iFlags, false, + false, -1, PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL); + GenericReceiver receiver = new ReplaceReceiver(ip.pkg.packageName); + int replaceFlags = rFlags | PackageManager.INSTALL_REPLACE_EXISTING; + try { + InstallParams rp = installFromRawResource("install.apk", rApk, + rFlags, false, + false, -1, PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL); + assertInstall(rp.pkg, replaceFlags, ip.pkg.installLocation); + } catch (Exception e) { + failStr("Failed with exception : " + e); + } finally { + cleanUpInstall(ip); + } } public void xxxtestClearAllSecureContainers() { @@ -1097,9 +1172,9 @@ public class PackageManagerTests extends AndroidTestCase { ApplicationInfo info = getPm().getApplicationInfo(ip.pkg.packageName, 0); assertNotNull(info); if ((moveFlags & PackageManager.MOVE_INTERNAL) != 0) { - assertTrue((info.flags & ApplicationInfo.FLAG_ON_SDCARD) == 0); + assertTrue((info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) == 0); } else if ((moveFlags & PackageManager.MOVE_EXTERNAL_MEDIA) != 0){ - assertTrue((info.flags & ApplicationInfo.FLAG_ON_SDCARD) != 0); + assertTrue((info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0); } } else { assertFalse(retCode); diff --git a/tools/aapt/Command.cpp b/tools/aapt/Command.cpp index 537ae5e..735a80d 100644 --- a/tools/aapt/Command.cpp +++ b/tools/aapt/Command.cpp @@ -603,7 +603,7 @@ int doDump(Bundle* bundle) } else { printf("versionCode='' "); } - String8 versionName = getAttribute(tree, VERSION_NAME_ATTR, &error); + String8 versionName = getResolvedAttribute(&res, tree, VERSION_NAME_ATTR, &error); if (error != "") { fprintf(stderr, "ERROR getting 'android:versionName' attribute: %s\n", error.string()); goto bail; diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp index b7580b3..c0ebb59 100644 --- a/tools/aapt/Resource.cpp +++ b/tools/aapt/Resource.cpp @@ -379,14 +379,64 @@ enum { ATTR_LEADING_SPACES = -3, ATTR_TRAILING_SPACES = -4 }; -static int validateAttr(const String8& path, const ResXMLParser& parser, +static int validateAttr(const String8& path, const ResTable& table, + const ResXMLParser& parser, const char* ns, const char* attr, const char* validChars, bool required) { size_t len; ssize_t index = parser.indexOfAttribute(ns, attr); const uint16_t* str; - if (index >= 0 && (str=parser.getAttributeStringValue(index, &len)) != NULL) { + Res_value value; + if (index >= 0 && parser.getAttributeValue(index, &value) >= 0) { + const ResStringPool* pool = &parser.getStrings(); + if (value.dataType == Res_value::TYPE_REFERENCE) { + uint32_t specFlags = 0; + int strIdx; + if ((strIdx=table.resolveReference(&value, 0x10000000, NULL, &specFlags)) < 0) { + fprintf(stderr, "%s:%d: Tag <%s> attribute %s references unknown resid 0x%08x.\n", + path.string(), parser.getLineNumber(), + String8(parser.getElementName(&len)).string(), attr, + value.data); + return ATTR_NOT_FOUND; + } + + pool = table.getTableStringBlock(strIdx); + #if 0 + if (pool != NULL) { + str = pool->stringAt(value.data, &len); + } + printf("***** RES ATTR: %s specFlags=0x%x strIdx=%d: %s\n", attr, + specFlags, strIdx, str != NULL ? String8(str).string() : "???"); + #endif + if ((specFlags&~ResTable_typeSpec::SPEC_PUBLIC) != 0 && false) { + fprintf(stderr, "%s:%d: Tag <%s> attribute %s varies by configurations 0x%x.\n", + path.string(), parser.getLineNumber(), + String8(parser.getElementName(&len)).string(), attr, + specFlags); + return ATTR_NOT_FOUND; + } + } + if (value.dataType == Res_value::TYPE_STRING) { + if (pool == NULL) { + fprintf(stderr, "%s:%d: Tag <%s> attribute %s has no string block.\n", + path.string(), parser.getLineNumber(), + String8(parser.getElementName(&len)).string(), attr); + return ATTR_NOT_FOUND; + } + if ((str=pool->stringAt(value.data, &len)) == NULL) { + fprintf(stderr, "%s:%d: Tag <%s> attribute %s has corrupt string value.\n", + path.string(), parser.getLineNumber(), + String8(parser.getElementName(&len)).string(), attr); + return ATTR_NOT_FOUND; + } + } else { + fprintf(stderr, "%s:%d: Tag <%s> attribute %s has invalid type %d.\n", + path.string(), parser.getLineNumber(), + String8(parser.getElementName(&len)).string(), attr, + value.dataType); + return ATTR_NOT_FOUND; + } if (validChars) { for (size_t i=0; i<len; i++) { uint16_t c = str[i]; @@ -959,21 +1009,102 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets) err = NO_ERROR; } + if (table.validateLocalizations()) { + hasErrors = true; + } + + if (hasErrors) { + return UNKNOWN_ERROR; + } + const sp<AaptFile> manifestFile(androidManifestFile->getFiles().valueAt(0)); String8 manifestPath(manifestFile->getPrintableSource()); + // Generate final compiled manifest file. + manifestFile->clearData(); + sp<XMLNode> manifestTree = XMLNode::parse(manifestFile); + if (manifestTree == NULL) { + return UNKNOWN_ERROR; + } + err = massageManifest(bundle, manifestTree); + if (err < NO_ERROR) { + return err; + } + err = compileXmlFile(assets, manifestTree, manifestFile, &table); + if (err < NO_ERROR) { + return err; + } + + //block.restart(); + //printXMLBlock(&block); + + // -------------------------------------------------------------- + // Generate the final resource table. + // Re-flatten because we may have added new resource IDs + // -------------------------------------------------------------- + + ResTable finalResTable; + sp<AaptFile> resFile; + + if (table.hasResources()) { + sp<AaptSymbols> symbols = assets->getSymbolsFor(String8("R")); + err = table.addSymbols(symbols); + if (err < NO_ERROR) { + return err; + } + + resFile = getResourceFile(assets); + if (resFile == NULL) { + fprintf(stderr, "Error: unable to generate entry for resource data\n"); + return UNKNOWN_ERROR; + } + + err = table.flatten(bundle, resFile); + if (err < NO_ERROR) { + return err; + } + + if (bundle->getPublicOutputFile()) { + FILE* fp = fopen(bundle->getPublicOutputFile(), "w+"); + if (fp == NULL) { + fprintf(stderr, "ERROR: Unable to open public definitions output file %s: %s\n", + (const char*)bundle->getPublicOutputFile(), strerror(errno)); + return UNKNOWN_ERROR; + } + if (bundle->getVerbose()) { + printf(" Writing public definitions to %s.\n", bundle->getPublicOutputFile()); + } + table.writePublicDefinitions(String16(assets->getPackage()), fp); + fclose(fp); + } + + // Read resources back in, + finalResTable.add(resFile->getData(), resFile->getSize(), NULL); + +#if 0 + NOISY( + printf("Generated resources:\n"); + finalResTable.print(); + ) +#endif + } + // Perform a basic validation of the manifest file. This time we // parse it with the comments intact, so that we can use them to // generate java docs... so we are not going to write this one // back out to the final manifest data. - err = compileXmlFile(assets, manifestFile, &table, + sp<AaptFile> outManifestFile = new AaptFile(manifestFile->getSourceFile(), + manifestFile->getGroupEntry(), + manifestFile->getResourceType()); + err = compileXmlFile(assets, manifestFile, + outManifestFile, &table, XML_COMPILE_ASSIGN_ATTRIBUTE_IDS | XML_COMPILE_STRIP_WHITESPACE | XML_COMPILE_STRIP_RAW_VALUES); if (err < NO_ERROR) { return err; } ResXMLTree block; - block.setTo(manifestFile->getData(), manifestFile->getSize(), true); + block.setTo(outManifestFile->getData(), outManifestFile->getSize(), true); String16 manifest16("manifest"); String16 permission16("permission"); String16 permission_group16("permission-group"); @@ -1012,16 +1143,20 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets) continue; } if (strcmp16(block.getElementName(&len), manifest16.string()) == 0) { - if (validateAttr(manifestPath, block, NULL, "package", + if (validateAttr(manifestPath, finalResTable, block, NULL, "package", packageIdentChars, true) != ATTR_OKAY) { hasErrors = true; } + if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE, + "sharedUserId", packageIdentChars, false) != ATTR_OKAY) { + hasErrors = true; + } } else if (strcmp16(block.getElementName(&len), permission16.string()) == 0 || strcmp16(block.getElementName(&len), permission_group16.string()) == 0) { const bool isGroup = strcmp16(block.getElementName(&len), permission_group16.string()) == 0; - if (validateAttr(manifestPath, block, RESOURCES_ANDROID_NAMESPACE, "name", - isGroup ? packageIdentCharsWithTheStupid + if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE, + "name", isGroup ? packageIdentCharsWithTheStupid : packageIdentChars, true) != ATTR_OKAY) { hasErrors = true; } @@ -1099,56 +1234,56 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets) } syms->makeSymbolPublic(String8(e), srcPos); } else if (strcmp16(block.getElementName(&len), uses_permission16.string()) == 0) { - if (validateAttr(manifestPath, block, RESOURCES_ANDROID_NAMESPACE, "name", - packageIdentChars, true) != ATTR_OKAY) { + if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE, + "name", packageIdentChars, true) != ATTR_OKAY) { hasErrors = true; } } else if (strcmp16(block.getElementName(&len), instrumentation16.string()) == 0) { - if (validateAttr(manifestPath, block, RESOURCES_ANDROID_NAMESPACE, "name", - classIdentChars, true) != ATTR_OKAY) { + if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE, + "name", classIdentChars, true) != ATTR_OKAY) { hasErrors = true; } - if (validateAttr(manifestPath, block, + if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE, "targetPackage", packageIdentChars, true) != ATTR_OKAY) { hasErrors = true; } } else if (strcmp16(block.getElementName(&len), application16.string()) == 0) { - if (validateAttr(manifestPath, block, RESOURCES_ANDROID_NAMESPACE, "name", - classIdentChars, false) != ATTR_OKAY) { + if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE, + "name", classIdentChars, false) != ATTR_OKAY) { hasErrors = true; } - if (validateAttr(manifestPath, block, + if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE, "permission", packageIdentChars, false) != ATTR_OKAY) { hasErrors = true; } - if (validateAttr(manifestPath, block, + if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE, "process", processIdentChars, false) != ATTR_OKAY) { hasErrors = true; } - if (validateAttr(manifestPath, block, + if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE, "taskAffinity", processIdentChars, false) != ATTR_OKAY) { hasErrors = true; } } else if (strcmp16(block.getElementName(&len), provider16.string()) == 0) { - if (validateAttr(manifestPath, block, RESOURCES_ANDROID_NAMESPACE, "name", - classIdentChars, true) != ATTR_OKAY) { + if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE, + "name", classIdentChars, true) != ATTR_OKAY) { hasErrors = true; } - if (validateAttr(manifestPath, block, + if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE, "authorities", authoritiesIdentChars, true) != ATTR_OKAY) { hasErrors = true; } - if (validateAttr(manifestPath, block, + if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE, "permission", packageIdentChars, false) != ATTR_OKAY) { hasErrors = true; } - if (validateAttr(manifestPath, block, + if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE, "process", processIdentChars, false) != ATTR_OKAY) { hasErrors = true; @@ -1156,39 +1291,39 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets) } else if (strcmp16(block.getElementName(&len), service16.string()) == 0 || strcmp16(block.getElementName(&len), receiver16.string()) == 0 || strcmp16(block.getElementName(&len), activity16.string()) == 0) { - if (validateAttr(manifestPath, block, RESOURCES_ANDROID_NAMESPACE, "name", - classIdentChars, true) != ATTR_OKAY) { + if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE, + "name", classIdentChars, true) != ATTR_OKAY) { hasErrors = true; } - if (validateAttr(manifestPath, block, + if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE, "permission", packageIdentChars, false) != ATTR_OKAY) { hasErrors = true; } - if (validateAttr(manifestPath, block, + if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE, "process", processIdentChars, false) != ATTR_OKAY) { hasErrors = true; } - if (validateAttr(manifestPath, block, + if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE, "taskAffinity", processIdentChars, false) != ATTR_OKAY) { hasErrors = true; } } else if (strcmp16(block.getElementName(&len), action16.string()) == 0 || strcmp16(block.getElementName(&len), category16.string()) == 0) { - if (validateAttr(manifestPath, block, + if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE, "name", packageIdentChars, true) != ATTR_OKAY) { hasErrors = true; } } else if (strcmp16(block.getElementName(&len), data16.string()) == 0) { - if (validateAttr(manifestPath, block, + if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE, "mimeType", typeIdentChars, true) != ATTR_OKAY) { hasErrors = true; } - if (validateAttr(manifestPath, block, + if (validateAttr(manifestPath, finalResTable, block, RESOURCES_ANDROID_NAMESPACE, "scheme", schemeIdentChars, true) != ATTR_OKAY) { hasErrors = true; @@ -1197,76 +1332,7 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets) } } - if (table.validateLocalizations()) { - hasErrors = true; - } - - if (hasErrors) { - return UNKNOWN_ERROR; - } - - // Generate final compiled manifest file. - manifestFile->clearData(); - sp<XMLNode> manifestTree = XMLNode::parse(manifestFile); - if (manifestTree == NULL) { - return UNKNOWN_ERROR; - } - err = massageManifest(bundle, manifestTree); - if (err < NO_ERROR) { - return err; - } - err = compileXmlFile(assets, manifestTree, manifestFile, &table); - if (err < NO_ERROR) { - return err; - } - - //block.restart(); - //printXMLBlock(&block); - - // -------------------------------------------------------------- - // Generate the final resource table. - // Re-flatten because we may have added new resource IDs - // -------------------------------------------------------------- - - if (table.hasResources()) { - sp<AaptSymbols> symbols = assets->getSymbolsFor(String8("R")); - err = table.addSymbols(symbols); - if (err < NO_ERROR) { - return err; - } - - sp<AaptFile> resFile(getResourceFile(assets)); - if (resFile == NULL) { - fprintf(stderr, "Error: unable to generate entry for resource data\n"); - return UNKNOWN_ERROR; - } - - err = table.flatten(bundle, resFile); - if (err < NO_ERROR) { - return err; - } - - if (bundle->getPublicOutputFile()) { - FILE* fp = fopen(bundle->getPublicOutputFile(), "w+"); - if (fp == NULL) { - fprintf(stderr, "ERROR: Unable to open public definitions output file %s: %s\n", - (const char*)bundle->getPublicOutputFile(), strerror(errno)); - return UNKNOWN_ERROR; - } - if (bundle->getVerbose()) { - printf(" Writing public definitions to %s.\n", bundle->getPublicOutputFile()); - } - table.writePublicDefinitions(String16(assets->getPackage()), fp); - fclose(fp); - } -#if 0 - NOISY( - ResTable rt; - rt.add(resFile->getData(), resFile->getSize(), NULL); - printf("Generated resources:\n"); - rt.print(); - ) -#endif + if (resFile != NULL) { // These resources are now considered to be a part of the included // resources, for others to reference. err = assets->addIncludedResources(resFile); @@ -1275,6 +1341,7 @@ status_t buildResources(Bundle* bundle, const sp<AaptAssets>& assets) return err; } } + return err; } diff --git a/tools/aapt/ResourceTable.cpp b/tools/aapt/ResourceTable.cpp index 1f9d152..ab5e937 100644 --- a/tools/aapt/ResourceTable.cpp +++ b/tools/aapt/ResourceTable.cpp @@ -28,6 +28,20 @@ status_t compileXmlFile(const sp<AaptAssets>& assets, } status_t compileXmlFile(const sp<AaptAssets>& assets, + const sp<AaptFile>& target, + const sp<AaptFile>& outTarget, + ResourceTable* table, + int options) +{ + sp<XMLNode> root = XMLNode::parse(target); + if (root == NULL) { + return UNKNOWN_ERROR; + } + + return compileXmlFile(assets, root, outTarget, table, options); +} + +status_t compileXmlFile(const sp<AaptAssets>& assets, const sp<XMLNode>& root, const sp<AaptFile>& target, ResourceTable* table, diff --git a/tools/aapt/ResourceTable.h b/tools/aapt/ResourceTable.h index 60d0901..186c7ca 100644 --- a/tools/aapt/ResourceTable.h +++ b/tools/aapt/ResourceTable.h @@ -37,6 +37,12 @@ status_t compileXmlFile(const sp<AaptAssets>& assets, int options = XML_COMPILE_STANDARD_RESOURCE); status_t compileXmlFile(const sp<AaptAssets>& assets, + const sp<AaptFile>& target, + const sp<AaptFile>& outTarget, + ResourceTable* table, + int options = XML_COMPILE_STANDARD_RESOURCE); + +status_t compileXmlFile(const sp<AaptAssets>& assets, const sp<XMLNode>& xmlTree, const sp<AaptFile>& target, ResourceTable* table, diff --git a/wifi/java/android/net/wifi/WifiMonitor.java b/wifi/java/android/net/wifi/WifiMonitor.java index 0928d2b..266d801 100644 --- a/wifi/java/android/net/wifi/WifiMonitor.java +++ b/wifi/java/android/net/wifi/WifiMonitor.java @@ -272,10 +272,8 @@ public class WifiMonitor { int connectTries = 0; while (true) { - synchronized (mWifiStateTracker) { - if (WifiNative.connectToSupplicant()) { - return true; - } + if (mWifiStateTracker.connectToSupplicant()) { + return true; } if (connectTries++ < 3) { nap(5); diff --git a/wifi/java/android/net/wifi/WifiNative.java b/wifi/java/android/net/wifi/WifiNative.java index c3c519f..f98cd28 100644 --- a/wifi/java/android/net/wifi/WifiNative.java +++ b/wifi/java/android/net/wifi/WifiNative.java @@ -26,6 +26,9 @@ import android.net.DhcpInfo; * <p/> * Note that methods whose names are not of the form "xxxCommand()" do * not talk to the supplicant daemon. + * Also, note that all WifiNative calls should happen in the + * WifiStateTracker class except for waitForEvent() call which is + * on a separate monitor channel for WifiMonitor * * {@hide} */ diff --git a/wifi/java/android/net/wifi/WifiStateTracker.java b/wifi/java/android/net/wifi/WifiStateTracker.java index 810e4d2..cc47d08 100644 --- a/wifi/java/android/net/wifi/WifiStateTracker.java +++ b/wifi/java/android/net/wifi/WifiStateTracker.java @@ -544,9 +544,7 @@ public class WifiStateTracker extends NetworkStateTracker { */ void notifyScanResultsAvailable() { // reset the supplicant's handling of scan results to "normal" mode - synchronized (this) { - WifiNative.setScanResultHandlingCommand(SUPPL_SCAN_HANDLING_NORMAL); - } + setScanResultHandling(SUPPL_SCAN_HANDLING_NORMAL); sendEmptyMessage(EVENT_SCAN_RESULTS_AVAILABLE); } @@ -613,39 +611,6 @@ public class WifiStateTracker extends NetworkStateTracker { } /** - * Set the number of allowed radio frequency channels from the system - * setting value, if any. - * @return {@code true} if the operation succeeds, {@code false} otherwise, e.g., - * the number of channels is invalid. - */ - public synchronized boolean setNumAllowedChannels() { - try { - return setNumAllowedChannels( - Settings.Secure.getInt(mContext.getContentResolver(), - Settings.Secure.WIFI_NUM_ALLOWED_CHANNELS)); - } catch (Settings.SettingNotFoundException e) { - if (mNumAllowedChannels != 0) { - WifiNative.setNumAllowedChannelsCommand(mNumAllowedChannels); - } - // otherwise, use the driver default - } - return true; - } - - /** - * Set the number of radio frequency channels that are allowed to be used - * in the current regulatory domain. - * @param numChannels the number of allowed channels. Must be greater than 0 - * and less than or equal to 16. - * @return {@code true} if the operation succeeds, {@code false} otherwise, e.g., - * {@code numChannels} is outside the valid range. - */ - public synchronized boolean setNumAllowedChannels(int numChannels) { - mNumAllowedChannels = numChannels; - return WifiNative.setNumAllowedChannelsCommand(numChannels); - } - - /** * Set the run state to either "normal" or "scan-only". * @param scanOnlyMode true if the new mode should be scan-only. */ @@ -655,29 +620,19 @@ public class WifiStateTracker extends NetworkStateTracker { int scanType = (scanOnlyMode ? SUPPL_SCAN_HANDLING_LIST_ONLY : SUPPL_SCAN_HANDLING_NORMAL); if (LOCAL_LOGD) Log.v(TAG, "Scan-only mode changing to " + scanOnlyMode + " scanType=" + scanType); - if (WifiNative.setScanResultHandlingCommand(scanType)) { + if (setScanResultHandling(scanType)) { mIsScanOnly = scanOnlyMode; if (!isDriverStopped()) { if (scanOnlyMode) { - WifiNative.disconnectCommand(); + disconnect(); } else { - WifiNative.reconnectCommand(); + reconnectCommand(); } } } } } - /** - * Enable or disable Bluetooth coexistence scan mode. When this mode is on, - * some of the low-level scan parameters used by the driver are changed to - * reduce interference with A2DP streaming. - * - * @param isBluetoothPlaying whether to enable or disable this mode - */ - public synchronized void setBluetoothScanMode(boolean isBluetoothPlaying) { - WifiNative.setBluetoothCoexistenceScanModeCommand(isBluetoothPlaying); - } private void checkIsBluetoothPlaying() { boolean isBluetoothPlaying = false; @@ -765,10 +720,8 @@ public class WifiStateTracker extends NetworkStateTracker { * The MAC address isn't going to change, so just request it * once here. */ - String macaddr; - synchronized (this) { - macaddr = WifiNative.getMacAddressCommand(); - } + String macaddr = getMacAddress(); + if (macaddr != null) { mWifiInfo.setMacAddress(macaddr); } @@ -831,9 +784,8 @@ public class WifiStateTracker extends NetworkStateTracker { // [ 1- 0] Connected to supplicant (1), disconnected from supplicant (0) , // or supplicant died (2) EventLog.writeEvent(EVENTLOG_SUPPLICANT_CONNECTION_STATE_CHANGED, died ? 2 : 0); - synchronized (this) { - WifiNative.closeSupplicantConnection(); - } + closeSupplicantConnection(); + if (died) { resetConnections(true); } @@ -861,9 +813,7 @@ public class WifiStateTracker extends NetworkStateTracker { // Only do this if we haven't gotten a new supplicant status since the timer // started if (mNumSupplicantStateChanges == msg.arg1) { - synchronized (this) { - WifiNative.scanCommand(false); // do a passive scan - } + scan(false); // do a passive scan } break; @@ -954,13 +904,9 @@ public class WifiStateTracker extends NetworkStateTracker { if (mRunState == RUN_STATE_RUNNING && !mIsScanOnly && networkId != -1) { sendMessageDelayed(reconnectMsg, RECONNECT_DELAY_MSECS); } else if (mRunState == RUN_STATE_STOPPING) { - synchronized (this) { - WifiNative.stopDriverCommand(); - } + stopDriver(); } else if (mRunState == RUN_STATE_STARTING && !mIsScanOnly) { - synchronized (this) { - WifiNative.reconnectCommand(); - } + reconnectCommand(); } } else if (newState == SupplicantState.DISCONNECTED) { mHaveIpAddress = false; @@ -1129,9 +1075,7 @@ public class WifiStateTracker extends NetworkStateTracker { } addToBlacklist(BSSID); } - synchronized(this) { - WifiNative.reconnectCommand(); - } + reconnectCommand(); } break; @@ -1174,9 +1118,7 @@ public class WifiStateTracker extends NetworkStateTracker { mHaveIpAddress = false; mWifiInfo.setIpAddress(0); mObtainingIpAddress = false; - synchronized(this) { - WifiNative.disconnectCommand(); - } + disconnect(); } break; @@ -1199,11 +1141,11 @@ public class WifiStateTracker extends NetworkStateTracker { if (mRunState == RUN_STATE_STARTING) { mRunState = RUN_STATE_RUNNING; if (!mIsScanOnly) { - WifiNative.reconnectCommand(); + reconnectCommand(); } else { // In some situations, supplicant needs to be kickstarted to // start the background scanning - WifiNative.scanCommand(true); + scan(true); } } } @@ -1245,12 +1187,6 @@ public class WifiStateTracker extends NetworkStateTracker { return disabledNetwork; } - public synchronized void setScanMode(boolean isScanModeActive) { - if (mIsScanModeActive != isScanModeActive) { - WifiNative.setScanModeCommand(mIsScanModeActive = isScanModeActive); - } - } - private void configureInterface() { checkPollTimer(); mLastSignalLevel = -1; @@ -1371,10 +1307,7 @@ public class WifiStateTracker extends NetworkStateTracker { } private void requestConnectionStatus(WifiInfo info) { - String reply; - synchronized (this) { - reply = WifiNative.statusCommand(); - } + String reply = status(); if (reply == null) { return; } @@ -1423,7 +1356,7 @@ public class WifiStateTracker extends NetworkStateTracker { */ private synchronized void requestPolledInfo(WifiInfo info, boolean polling) { - int newRssi = (polling ? WifiNative.getRssiApproxCommand() : WifiNative.getRssiCommand()); + int newRssi = (polling ? getRssiApprox() : getRssi()); if (newRssi != -1 && -200 < newRssi && newRssi < 256) { // screen out invalid values /* some implementations avoid negative values by adding 256 * so we need to adjust for that here. @@ -1451,7 +1384,7 @@ public class WifiStateTracker extends NetworkStateTracker { } else { info.setRssi(-200); } - int newLinkSpeed = WifiNative.getLinkSpeedCommand(); + int newLinkSpeed = getLinkSpeed(); if (newLinkSpeed != -1) { info.setLinkSpeed(newLinkSpeed); } @@ -1521,9 +1454,9 @@ public class WifiStateTracker extends NetworkStateTracker { mRunState = RUN_STATE_STOPPING; if (mWifiInfo.getSupplicantState() == SupplicantState.DORMANT) { - return WifiNative.stopDriverCommand(); + return stopDriver(); } else { - return WifiNative.disconnectCommand(); + return disconnect(); } } else { /* @@ -1547,17 +1480,213 @@ public class WifiStateTracker extends NetworkStateTracker { if (mRunState == RUN_STATE_STOPPED) { mRunState = RUN_STATE_STARTING; resetConnections(true); - return WifiNative.startDriverCommand(); + return startDriver(); } else if (mRunState == RUN_STATE_STOPPING) { mRunState = RUN_STATE_STARTING; } return true; } + /** + * TODO: add documentation to all the native calls + * along with conditional checks to make sure + * native calls dont happen when wifi is not enabled + */ + + public synchronized boolean loadDriver() { + return WifiNative.loadDriver(); + } + + public synchronized boolean unloadDriver() { + return WifiNative.unloadDriver(); + } + + public synchronized boolean startSupplicant() { + return WifiNative.startSupplicant(); + } + + public synchronized boolean stopSupplicant() { + return WifiNative.stopSupplicant(); + } + + public synchronized boolean connectToSupplicant() { + return WifiNative.connectToSupplicant(); + } + + public synchronized void closeSupplicantConnection() { + WifiNative.closeSupplicantConnection(); + } + + public synchronized boolean ping() { + return WifiNative.pingCommand(); + } + + public synchronized boolean scan(boolean forceActive) { + return WifiNative.scanCommand(forceActive); + } + + public synchronized boolean setScanResultHandling(int mode) { + return WifiNative.setScanResultHandlingCommand(mode); + } + + public synchronized String scanResults() { + return WifiNative.scanResultsCommand(); + } + + public synchronized void setScanMode(boolean isScanModeActive) { + if (mIsScanModeActive != isScanModeActive) { + WifiNative.setScanModeCommand(mIsScanModeActive = isScanModeActive); + } + } + + public synchronized boolean disconnect() { + return WifiNative.disconnectCommand(); + } + + public synchronized boolean reconnectCommand() { + return WifiNative.reconnectCommand(); + } + + public synchronized int addNetwork() { + return WifiNative.addNetworkCommand(); + } + public synchronized boolean removeNetwork(int networkId) { return mDisconnectExpected = WifiNative.removeNetworkCommand(networkId); } + public synchronized boolean enableNetwork(int netId, boolean disableOthers) { + return WifiNative.enableNetworkCommand(netId, disableOthers); + } + + public synchronized boolean disableNetwork(int netId) { + return WifiNative.disableNetworkCommand(netId); + } + + public synchronized boolean reassociate() { + return WifiNative.reassociateCommand(); + } + + public synchronized boolean addToBlacklist(String bssid) { + return WifiNative.addToBlacklistCommand(bssid); + } + + public synchronized boolean clearBlacklist() { + return WifiNative.clearBlacklistCommand(); + } + + public synchronized String listNetworks() { + return WifiNative.listNetworksCommand(); + } + + public synchronized String getNetworkVariable(int netId, String name) { + return WifiNative.getNetworkVariableCommand(netId, name); + } + + public synchronized boolean setNetworkVariable(int netId, String name, String value) { + return WifiNative.setNetworkVariableCommand(netId, name, value); + } + + public synchronized String status() { + return WifiNative.statusCommand(); + } + + public synchronized int getRssi() { + return WifiNative.getRssiApproxCommand(); + } + + public synchronized int getRssiApprox() { + return WifiNative.getRssiApproxCommand(); + } + + public synchronized int getLinkSpeed() { + return WifiNative.getLinkSpeedCommand(); + } + + public synchronized String getMacAddress() { + return WifiNative.getMacAddressCommand(); + } + + public synchronized boolean startDriver() { + return WifiNative.startDriverCommand(); + } + + public synchronized boolean stopDriver() { + return WifiNative.stopDriverCommand(); + } + + public synchronized boolean startPacketFiltering() { + return WifiNative.startPacketFiltering(); + } + + public synchronized boolean stopPacketFiltering() { + return WifiNative.stopPacketFiltering(); + } + + public synchronized boolean setPowerMode(int mode) { + return WifiNative.setPowerModeCommand(mode); + } + + /** + * Set the number of allowed radio frequency channels from the system + * setting value, if any. + * @return {@code true} if the operation succeeds, {@code false} otherwise, e.g., + * the number of channels is invalid. + */ + public synchronized boolean setNumAllowedChannels() { + try { + return setNumAllowedChannels( + Settings.Secure.getInt(mContext.getContentResolver(), + Settings.Secure.WIFI_NUM_ALLOWED_CHANNELS)); + } catch (Settings.SettingNotFoundException e) { + if (mNumAllowedChannels != 0) { + WifiNative.setNumAllowedChannelsCommand(mNumAllowedChannels); + } + // otherwise, use the driver default + } + return true; + } + + /** + * Set the number of radio frequency channels that are allowed to be used + * in the current regulatory domain. + * @param numChannels the number of allowed channels. Must be greater than 0 + * and less than or equal to 16. + * @return {@code true} if the operation succeeds, {@code false} otherwise, e.g., + * {@code numChannels} is outside the valid range. + */ + public synchronized boolean setNumAllowedChannels(int numChannels) { + mNumAllowedChannels = numChannels; + return WifiNative.setNumAllowedChannelsCommand(numChannels); + } + + public synchronized int getNumAllowedChannels() { + return WifiNative.getNumAllowedChannelsCommand(); + } + + public synchronized boolean setBluetoothCoexistenceMode(int mode) { + return WifiNative.setBluetoothCoexistenceModeCommand(mode); + } + + /** + * Enable or disable Bluetooth coexistence scan mode. When this mode is on, + * some of the low-level scan parameters used by the driver are changed to + * reduce interference with A2DP streaming. + * + * @param isBluetoothPlaying whether to enable or disable this mode + */ + public synchronized void setBluetoothScanMode(boolean isBluetoothPlaying) { + WifiNative.setBluetoothCoexistenceScanModeCommand(isBluetoothPlaying); + } + + public synchronized boolean saveConfig() { + return WifiNative.saveConfigCommand(); + } + + public synchronized boolean reloadConfig() { + return WifiNative.reloadConfigCommand(); + } + public boolean setRadio(boolean turnOn) { return mWM.setWifiEnabled(turnOn); } @@ -1571,7 +1700,7 @@ public class WifiStateTracker extends NetworkStateTracker { public int startUsingNetworkFeature(String feature, int callingPid, int callingUid) { return -1; } - + /** * {@inheritDoc} * There are currently no Wi-Fi-specific features supported. @@ -1706,18 +1835,6 @@ public class WifiStateTracker extends NetworkStateTracker { mNumScansSinceNetworkStateChange = 0; } - public synchronized boolean reassociate() { - return WifiNative.reassociateCommand(); - } - - public synchronized boolean addToBlacklist(String bssid) { - return WifiNative.addToBlacklistCommand(bssid); - } - - public synchronized boolean clearBlacklist() { - return WifiNative.clearBlacklistCommand(); - } - @Override public String toString() { StringBuffer sb = new StringBuffer(); @@ -1796,15 +1913,12 @@ public class WifiStateTracker extends NetworkStateTracker { modifiedBluetoothCoexistenceMode = true; // Disable the coexistence mode - synchronized (WifiStateTracker.this) { - WifiNative.setBluetoothCoexistenceModeCommand( - WifiNative.BLUETOOTH_COEXISTENCE_MODE_DISABLED); - } - } - - synchronized (WifiStateTracker.this) { - WifiNative.setPowerModeCommand(DRIVER_POWER_MODE_ACTIVE); + setBluetoothCoexistenceMode( + WifiNative.BLUETOOTH_COEXISTENCE_MODE_DISABLED); } + + setPowerMode(DRIVER_POWER_MODE_ACTIVE); + synchronized (this) { // A new request is being made, so assume we will callback mCancelCallback = false; @@ -1818,18 +1932,15 @@ public class WifiStateTracker extends NetworkStateTracker { Log.i(TAG, "DhcpHandler: DHCP request failed: " + NetworkUtils.getDhcpError()); } - synchronized (WifiStateTracker.this) { - WifiNative.setPowerModeCommand(DRIVER_POWER_MODE_AUTO); - } - + + setPowerMode(DRIVER_POWER_MODE_AUTO); + if (modifiedBluetoothCoexistenceMode) { // Set the coexistence mode back to its default value - synchronized (WifiStateTracker.this) { - WifiNative.setBluetoothCoexistenceModeCommand( - WifiNative.BLUETOOTH_COEXISTENCE_MODE_SENSE); - } + setBluetoothCoexistenceMode( + WifiNative.BLUETOOTH_COEXISTENCE_MODE_SENSE); } - + synchronized (this) { if (!mCancelCallback) { mTarget.sendEmptyMessage(event); @@ -1838,7 +1949,7 @@ public class WifiStateTracker extends NetworkStateTracker { break; } } - + public synchronized void setCancelCallback(boolean cancelCallback) { mCancelCallback = cancelCallback; } |