diff options
28 files changed, 1000 insertions, 398 deletions
diff --git a/api/current.xml b/api/current.xml index fa54a10..68a1c76 100644 --- a/api/current.xml +++ b/api/current.xml @@ -36906,6 +36906,17 @@ visibility="public" > </field> +<field name="METADATA_DOCK_HOME" + type="java.lang.String" + transient="false" + volatile="false" + value=""android.dock_home"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="URI_INTENT_SCHEME" type="int" transient="false" @@ -160428,6 +160439,17 @@ visibility="public" > </field> +<field name="FLAG_DISMISS_KEYGUARD" + type="int" + transient="false" + volatile="false" + value="4194304" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="FLAG_DITHER" type="int" transient="false" diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 8142d1a..6acd665 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -1765,6 +1765,7 @@ public final class ActivityThread { public static final int CREATE_BACKUP_AGENT = 128; public static final int DESTROY_BACKUP_AGENT = 129; public static final int SUICIDE = 130; + public static final int REMOVE_PROVIDER = 131; String codeToString(int code) { if (localLOGV) { switch (code) { @@ -1799,6 +1800,7 @@ public final class ActivityThread { case CREATE_BACKUP_AGENT: return "CREATE_BACKUP_AGENT"; case DESTROY_BACKUP_AGENT: return "DESTROY_BACKUP_AGENT"; case SUICIDE: return "SUICIDE"; + case REMOVE_PROVIDER: return "REMOVE_PROVIDER"; } } return "(unknown)"; @@ -1911,9 +1913,10 @@ public final class ActivityThread { handleDestroyBackupAgent((CreateBackupAgentData)msg.obj); break; case SUICIDE: - { - Process.killProcess(Process.myPid()); - } + Process.killProcess(Process.myPid()); + break; + case REMOVE_PROVIDER: + completeRemoveProvider((IContentProvider)msg.obj); break; } } @@ -4029,15 +4032,28 @@ public final class ActivityThread { } else { prc.count--; if(prc.count == 0) { - mProviderRefCountMap.remove(jBinder); - //invoke removeProvider to dereference provider - removeProviderLocked(provider); + // Schedule the actual remove asynchronously, since we + // don't know the context this will be called in. + Message msg = mH.obtainMessage(H.REMOVE_PROVIDER, provider); + mH.sendMessage(msg); } //end if } //end else } //end synchronized return true; } + final void completeRemoveProvider(IContentProvider provider) { + IBinder jBinder = provider.asBinder(); + synchronized(mProviderMap) { + ProviderRefCount prc = mProviderRefCountMap.get(jBinder); + if(prc != null && prc.count == 0) { + mProviderRefCountMap.remove(jBinder); + //invoke removeProvider to dereference provider + removeProviderLocked(provider); + } + } + } + public final void removeProviderLocked(IContentProvider provider) { if (provider == null) { return; diff --git a/core/java/android/app/SearchDialog.java b/core/java/android/app/SearchDialog.java index c0a0480..a2c95f4 100644 --- a/core/java/android/app/SearchDialog.java +++ b/core/java/android/app/SearchDialog.java @@ -1617,6 +1617,9 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS // Now build the Intent Intent intent = new Intent(action); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + // We need CLEAR_TOP to avoid reusing an old task that has other activities + // on top of the one we want. + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); if (data != null) { intent.setData(data); } diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index f6ca50d..5fb5768 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -1885,7 +1885,7 @@ public class Intent implements Parcelable { "android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST"; /** * An activity to run when device is inserted into a car dock. - * Used with {@link #ACTION_MAIN} to launch an activity. + * Used with {@link #ACTION_MAIN} to launch an activity. * To monitor dock state, use {@link #ACTION_DOCK_EVENT} instead. */ @SdkConstant(SdkConstantType.INTENT_CATEGORY) @@ -2056,6 +2056,12 @@ public class Intent implements Parcelable { public static final int EXTRA_DOCK_STATE_CAR = 2; /** + * Boolean that can be supplied as meta-data with a dock activity, to + * indicate that the dock should take over the home key when it is active. + */ + public static final String METADATA_DOCK_HOME = "android.dock_home"; + + /** * Used as a parcelable extra field in {@link #ACTION_APP_ERROR}, containing * the bug report. * @@ -3605,7 +3611,7 @@ public class Intent implements Parcelable { } } else { ResolveInfo info = pm.resolveActivity( - this, PackageManager.MATCH_DEFAULT_ONLY); + this, PackageManager.MATCH_DEFAULT_ONLY | flags); if (info != null) { ai = info.activityInfo; } diff --git a/core/java/android/server/BluetoothEventLoop.java b/core/java/android/server/BluetoothEventLoop.java index f9ab31c..f3bc3a6 100644 --- a/core/java/android/server/BluetoothEventLoop.java +++ b/core/java/android/server/BluetoothEventLoop.java @@ -247,7 +247,6 @@ class BluetoothEventLoop { addDevice(address, properties); } } - mBluetoothService.getBondState().setBondState(address, BluetoothDevice.BOND_BONDING); return; } @@ -404,6 +403,9 @@ class BluetoothEventLoop { mBluetoothService.cancelPairingUserInput(address); return null; } + // Set state to BONDING, for incoming connections it will be set here. + // For outgoing connections, it gets set when call createBond. + mBluetoothService.getBondState().setBondState(address, BluetoothDevice.BOND_BONDING); return address; } diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index f67c4aa..396e380 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -488,7 +488,10 @@ public interface WindowManager extends ViewManager { * is locked. This will let application windows take precedence over * key guard or any other lock screens. Can be used with * {@link #FLAG_KEEP_SCREEN_ON} to turn screen on and display windows - * directly before showing the key guard window + * directly before showing the key guard window. Can be used with + * {@link #FLAG_DISMISS_KEYGUARD} to automatically fully dismisss + * non-secure keyguards. This flag only applies to the top-most + * full-screen window. */ public static final int FLAG_SHOW_WHEN_LOCKED = 0x00080000; @@ -506,6 +509,19 @@ public interface WindowManager extends ViewManager { * up the device) to turn the screen on. */ public static final int FLAG_TURN_SCREEN_ON = 0x00200000; + /** Window flag: when set the window will cause the keyguard to + * be dismissed, only if it is not a secure lock keyguard. Because such + * a keyguard is not needed for security, it will never re-appear if + * the user navigates to another window (in contrast to + * {@link #FLAG_SHOW_WHEN_LOCKED}, which will only temporarily + * hide both secure and non-secure keyguards but ensure they reappear + * when the user moves to another UI that doesn't hide them). + * If the keyguard is currently active and is secure (requires an + * unlock pattern) than the user will still need to confirm it before + * seeing this window, unless {@link #FLAG_SHOW_WHEN_LOCKED} has + * also been set. */ + public static final int FLAG_DISMISS_KEYGUARD = 0x00400000; + /** Window flag: special flag to limit the size of the window to be * original size ([320x480] x density). Used to create window for applications * running under compatibility mode. diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java index cc5aeb1..1923743 100644 --- a/core/java/android/view/WindowManagerPolicy.java +++ b/core/java/android/view/WindowManagerPolicy.java @@ -602,11 +602,18 @@ public interface WindowManagerPolicy { * returned, all windows given to layoutWindow() <em>must</em> have had a * frame assigned. * - * @return Return true if layout state may have changed (so that another - * layout will be performed). + * @return Return any bit set of {@link #FINISH_LAYOUT_REDO_LAYOUT} + * and {@link #FINISH_LAYOUT_REDO_CONFIG}. */ - public boolean finishLayoutLw(); + public int finishLayoutLw(); + /** Layout state may have changed (so another layout will be performed) */ + static final int FINISH_LAYOUT_REDO_LAYOUT = 0x0001; + /** Configuration state may have changed */ + static final int FINISH_LAYOUT_REDO_CONFIG = 0x0002; + /** Wallpaper may need to move */ + static final int FINISH_LAYOUT_REDO_WALLPAPER = 0x0004; + /** * Called when animation of the windows is about to start. * diff --git a/core/java/android/webkit/BrowserFrame.java b/core/java/android/webkit/BrowserFrame.java index 4204a1a..e233a02 100644 --- a/core/java/android/webkit/BrowserFrame.java +++ b/core/java/android/webkit/BrowserFrame.java @@ -605,8 +605,8 @@ class BrowserFrame extends Handler { } // Called by JNI when an apple-touch-icon attribute was found. - private void didReceiveTouchIconUrl(String url) { - mCallbackProxy.onReceivedTouchIconUrl(url); + private void didReceiveTouchIconUrl(String url, boolean precomposed) { + mCallbackProxy.onReceivedTouchIconUrl(url, precomposed); } /** diff --git a/core/java/android/webkit/CallbackProxy.java b/core/java/android/webkit/CallbackProxy.java index b051675..1ec769b 100644 --- a/core/java/android/webkit/CallbackProxy.java +++ b/core/java/android/webkit/CallbackProxy.java @@ -249,7 +249,7 @@ class CallbackProxy extends Handler { case RECEIVED_TOUCH_ICON_URL: if (mWebChromeClient != null) { mWebChromeClient.onReceivedTouchIconUrl(mWebView, - (String) msg.obj); + (String) msg.obj, msg.arg1 == 1); } break; @@ -1065,19 +1065,22 @@ class CallbackProxy extends Handler { sendMessage(obtainMessage(RECEIVED_ICON, icon)); } - /* package */ void onReceivedTouchIconUrl(String url) { + /* package */ void onReceivedTouchIconUrl(String url, boolean precomposed) { // We should have a current item but we do not want to crash so check // for null. WebHistoryItem i = mBackForwardList.getCurrentItem(); if (i != null) { - i.setTouchIconUrl(url); + if (precomposed || i.getTouchIconUrl() != null) { + i.setTouchIconUrl(url); + } } // Do an unsynchronized quick check to avoid posting if no callback has // been set. if (mWebChromeClient == null) { return; } - sendMessage(obtainMessage(RECEIVED_TOUCH_ICON_URL, url)); + sendMessage(obtainMessage(RECEIVED_TOUCH_ICON_URL, + precomposed ? 1 : 0, 0, url)); } public void onReceivedTitle(String title) { diff --git a/core/java/android/webkit/GeolocationPermissions.java b/core/java/android/webkit/GeolocationPermissions.java index e985ccc..c0cac01 100755 --- a/core/java/android/webkit/GeolocationPermissions.java +++ b/core/java/android/webkit/GeolocationPermissions.java @@ -19,10 +19,9 @@ package android.webkit; import android.os.Handler; import android.os.Message; import android.util.Log; -import java.util.concurrent.locks.Condition; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReentrantLock; +import java.util.HashMap; import java.util.HashSet; +import java.util.Map; import java.util.Set; @@ -47,15 +46,13 @@ public final class GeolocationPermissions { private static GeolocationPermissions sInstance; private Handler mHandler; + private Handler mUIHandler; // Members used to transfer the origins and permissions between threads. private Set<String> mOrigins; private boolean mAllowed; private Set<String> mOriginsToClear; private Set<String> mOriginsToAllow; - private static Lock mLock = new ReentrantLock(); - private static boolean mUpdated; - private static Condition mUpdatedCondition = mLock.newCondition(); // Message ids static final int GET_ORIGINS = 0; @@ -64,6 +61,15 @@ public final class GeolocationPermissions { static final int ALLOW = 3; static final int CLEAR_ALL = 4; + // Message ids on the UI thread + static final int RETURN_ORIGINS = 0; + static final int RETURN_ALLOWED = 1; + + private static final String ORIGINS = "origins"; + private static final String ORIGIN = "origin"; + private static final String CALLBACK = "callback"; + private static final String ALLOWED = "allowed"; + /** * Gets the singleton instance of the class. */ @@ -75,22 +81,62 @@ public final class GeolocationPermissions { } /** + * Creates the UI message handler. Must be called on the UI thread. + */ + public void createUIHandler() { + if (mUIHandler == null) { + mUIHandler = new Handler() { + @Override + public void handleMessage(Message msg) { + // Runs on the UI thread. + switch (msg.what) { + case RETURN_ORIGINS: { + Map values = (Map) msg.obj; + Set origins = (Set) values.get(ORIGINS); + ValueCallback<Set> callback = (ValueCallback<Set>) values.get(CALLBACK); + callback.onReceiveValue(origins); + } break; + case RETURN_ALLOWED: { + Map values = (Map) msg.obj; + Boolean allowed = (Boolean) values.get(ALLOWED); + ValueCallback<Boolean> callback = (ValueCallback<Boolean>) values.get(CALLBACK); + callback.onReceiveValue(allowed); + } break; + } + } + }; + } + } + + /** * Creates the message handler. Must be called on the WebKit thread. */ public void createHandler() { - mLock.lock(); if (mHandler == null) { mHandler = new Handler() { @Override public void handleMessage(Message msg) { // Runs on the WebKit thread. switch (msg.what) { - case GET_ORIGINS: + case GET_ORIGINS: { getOriginsImpl(); - break; - case GET_ALLOWED: - getAllowedImpl((String) msg.obj); - break; + ValueCallback callback = (ValueCallback) msg.obj; + Set origins = new HashSet(mOrigins); + Map values = new HashMap<String, Object>(); + values.put(CALLBACK, callback); + values.put(ORIGINS, origins); + postUIMessage(Message.obtain(null, RETURN_ORIGINS, values)); + } break; + case GET_ALLOWED: { + Map values = (Map) msg.obj; + String origin = (String) values.get(ORIGIN); + ValueCallback callback = (ValueCallback) values.get(CALLBACK); + getAllowedImpl(origin); + Map retValues = new HashMap<String, Object>(); + retValues.put(CALLBACK, callback); + retValues.put(ALLOWED, new Boolean(mAllowed)); + postUIMessage(Message.obtain(null, RETURN_ALLOWED, retValues)); + } break; case CLEAR: nativeClear((String) msg.obj); break; @@ -115,7 +161,6 @@ public final class GeolocationPermissions { } } } - mLock.unlock(); } /** @@ -127,29 +172,31 @@ public final class GeolocationPermissions { } /** + * Utility function to send a message to the handler on the UI thread + */ + private void postUIMessage(Message msg) { + if (mUIHandler != null) { + mUIHandler.sendMessage(msg); + } + } + + /** * Gets the set of origins for which Geolocation permissions are stored. * Note that we represent the origins as strings. These are created using * WebCore::SecurityOrigin::toString(). As long as all 'HTML 5 modules' * (Database, Geolocation etc) do so, it's safe to match up origins for the * purposes of displaying UI. */ - public Set getOrigins() { - // Called on the UI thread. - Set origins = null; - mLock.lock(); - try { - mUpdated = false; - postMessage(Message.obtain(null, GET_ORIGINS)); - while (!mUpdated) { - mUpdatedCondition.await(); + public void getOrigins(ValueCallback<Set> callback) { + if (callback != null) { + if (WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName())) { + getOriginsImpl(); + Set origins = new HashSet(mOrigins); + callback.onReceiveValue(origins); + } else { + postMessage(Message.obtain(null, GET_ORIGINS, callback)); } - origins = mOrigins; - } catch (InterruptedException e) { - Log.e(TAG, "Exception while waiting for update", e); - } finally { - mLock.unlock(); } - return origins; } /** @@ -157,33 +204,29 @@ public final class GeolocationPermissions { */ private void getOriginsImpl() { // Called on the WebKit thread. - mLock.lock(); mOrigins = nativeGetOrigins(); - mUpdated = true; - mUpdatedCondition.signal(); - mLock.unlock(); } /** * Gets the permission state for the specified origin. */ - public boolean getAllowed(String origin) { - // Called on the UI thread. - boolean allowed = false; - mLock.lock(); - try { - mUpdated = false; - postMessage(Message.obtain(null, GET_ALLOWED, origin)); - while (!mUpdated) { - mUpdatedCondition.await(); - } - allowed = mAllowed; - } catch (InterruptedException e) { - Log.e(TAG, "Exception while waiting for update", e); - } finally { - mLock.unlock(); + public void getAllowed(String origin, ValueCallback<Boolean> callback) { + if (callback == null) { + return; + } + if (origin == null) { + callback.onReceiveValue(null); + return; + } + if (WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName())) { + getAllowedImpl(origin); + callback.onReceiveValue(new Boolean(mAllowed)); + } else { + Map values = new HashMap<String, Object>(); + values.put(ORIGIN, origin); + values.put(CALLBACK, callback); + postMessage(Message.obtain(null, GET_ALLOWED, values)); } - return allowed; } /** @@ -191,11 +234,7 @@ public final class GeolocationPermissions { */ private void getAllowedImpl(String origin) { // Called on the WebKit thread. - mLock.lock(); mAllowed = nativeGetAllowed(origin); - mUpdated = true; - mUpdatedCondition.signal(); - mLock.unlock(); } /** @@ -205,7 +244,6 @@ public final class GeolocationPermissions { */ public void clear(String origin) { // Called on the UI thread. - mLock.lock(); if (mHandler == null) { if (mOriginsToClear == null) { mOriginsToClear = new HashSet<String>(); @@ -217,7 +255,6 @@ public final class GeolocationPermissions { } else { postMessage(Message.obtain(null, CLEAR, origin)); } - mLock.unlock(); } /** @@ -227,7 +264,6 @@ public final class GeolocationPermissions { */ public void allow(String origin) { // Called on the UI thread. - mLock.lock(); if (mHandler == null) { if (mOriginsToAllow == null) { mOriginsToAllow = new HashSet<String>(); @@ -239,7 +275,6 @@ public final class GeolocationPermissions { } else { postMessage(Message.obtain(null, ALLOW, origin)); } - mLock.unlock(); } /** diff --git a/core/java/android/webkit/GoogleLocationSettingManager.java b/core/java/android/webkit/GoogleLocationSettingManager.java index fe7fce6..1b6e77c 100644 --- a/core/java/android/webkit/GoogleLocationSettingManager.java +++ b/core/java/android/webkit/GoogleLocationSettingManager.java @@ -35,8 +35,6 @@ import java.util.HashSet; * @hide pending API council review */ class GoogleLocationSettingManager { - // The application context. - private Context mContext; // The observer used to listen to the system setting. private GoogleLocationSettingObserver mSettingObserver; @@ -48,6 +46,8 @@ class GoogleLocationSettingManager { // by the browser. private final static String LAST_READ_USE_LOCATION_FOR_SERVICES = "lastReadUseLocationForServices"; + // The Browser package name. + private static final String BROWSER_PACKAGE_NAME = "com.android.browser"; // The Google origins we consider. private static HashSet<String> sGoogleOrigins; static { @@ -57,38 +57,72 @@ class GoogleLocationSettingManager { sGoogleOrigins.add("http://www.google.co.uk"); } - GoogleLocationSettingManager(Context context) { - mContext = context; + private static GoogleLocationSettingManager sGoogleLocationSettingManager = null; + private static int sRefCount = 0; + + static GoogleLocationSettingManager getInstance() { + if (sGoogleLocationSettingManager == null) { + sGoogleLocationSettingManager = new GoogleLocationSettingManager(); + } + return sGoogleLocationSettingManager; } + private GoogleLocationSettingManager() {} + /** * Starts the manager. Checks whether the setting has changed and * installs an observer to listen for future changes. */ - public void start() { - maybeApplySetting(); - + public void start(Context context) { + // Are we running in the browser? + if (context == null || !BROWSER_PACKAGE_NAME.equals(context.getPackageName())) { + return; + } + // Increase the refCount + sRefCount++; + // Are we already registered? + if (mSettingObserver != null) { + return; + } + // Read and apply the settings if needed. + maybeApplySetting(context); + // Register to receive notifications when the system settings change. mSettingObserver = new GoogleLocationSettingObserver(); - mSettingObserver.observe(); + mSettingObserver.observe(context); } /** + * Stops the manager. + */ + public void stop() { + // Are we already registered? + if (mSettingObserver == null) { + return; + } + if (--sRefCount == 0) { + mSettingObserver.doNotObserve(); + mSettingObserver = null; + } + } + /** * Checks to see if the system setting has changed and if so, * updates the Geolocation permissions accordingly. + * @param the Application context */ - private void maybeApplySetting() { - int setting = getSystemSetting(); - if (settingChanged(setting)) { + private void maybeApplySetting(Context context) { + int setting = getSystemSetting(context); + if (settingChanged(setting, context)) { applySetting(setting); } } /** * Gets the current system setting for 'Use location for Google services'. + * @param the Application context * @return The system setting. */ - private int getSystemSetting() { - return Settings.Secure.getInt(mContext.getContentResolver(), + private int getSystemSetting(Context context) { + return Settings.Secure.getInt(context.getContentResolver(), Settings.Secure.USE_LOCATION_FOR_SERVICES, sSystemSettingFalse); } @@ -97,12 +131,13 @@ class GoogleLocationSettingManager { * Determines whether the supplied setting has changed from the last * value read by the browser. * @param setting The setting. + * @param the Application context * @return Whether the setting has changed from the last value read * by the browser. */ - private boolean settingChanged(int setting) { + private boolean settingChanged(int setting, Context context) { SharedPreferences preferences = - PreferenceManager.getDefaultSharedPreferences(mContext); + PreferenceManager.getDefaultSharedPreferences(context); // Default to false. If the system setting is false the first time it is ever read by the // browser, there's nothing to do. int lastReadSetting = sSystemSettingFalse; @@ -137,20 +172,35 @@ class GoogleLocationSettingManager { * This class implements an observer to listen for changes to the * system setting. */ - class GoogleLocationSettingObserver extends ContentObserver { + private class GoogleLocationSettingObserver extends ContentObserver { + private Context mContext; + GoogleLocationSettingObserver() { super(new Handler()); } - void observe() { - ContentResolver resolver = mContext.getContentResolver(); + void observe(Context context) { + if (mContext != null) { + return; + } + ContentResolver resolver = context.getContentResolver(); resolver.registerContentObserver(Settings.Secure.getUriFor( Settings.Secure.USE_LOCATION_FOR_SERVICES), false, this); + mContext = context; + } + + void doNotObserve() { + if (mContext == null) { + return; + } + ContentResolver resolver = mContext.getContentResolver(); + resolver.unregisterContentObserver(this); + mContext = null; } @Override public void onChange(boolean selfChange) { - maybeApplySetting(); + maybeApplySetting(mContext); } } } diff --git a/core/java/android/webkit/PluginManager.java b/core/java/android/webkit/PluginManager.java index 32eea5f..766bd75 100644 --- a/core/java/android/webkit/PluginManager.java +++ b/core/java/android/webkit/PluginManager.java @@ -63,8 +63,11 @@ public class PluginManager { private final Context mContext; + private ArrayList<PackageInfo> mPackageInfoCache; + private PluginManager(Context context) { mContext = context; + mPackageInfoCache = new ArrayList<PackageInfo>(); } public static synchronized PluginManager getInstance(Context context) { @@ -92,65 +95,94 @@ public class PluginManager { } String[] getPluginDirectories() { + ArrayList<String> directories = new ArrayList<String>(); PackageManager pm = mContext.getPackageManager(); List<ResolveInfo> plugins = pm.queryIntentServices(new Intent( PLUGIN_ACTION), PackageManager.GET_SERVICES); - for (ResolveInfo info : plugins) { - ServiceInfo serviceInfo = info.serviceInfo; - if (serviceInfo == null) { - Log.w(LOGTAG, "Ignore bad plugin"); - continue; - } - PackageInfo pkgInfo; - try { - pkgInfo = pm.getPackageInfo(serviceInfo.packageName, - PackageManager.GET_PERMISSIONS - | PackageManager.GET_SIGNATURES); - } catch (NameNotFoundException e) { - Log.w(LOGTAG, "Cant find plugin: " + serviceInfo.packageName); - continue; - } - if (pkgInfo == null) { - continue; - } - String directory = pkgInfo.applicationInfo.dataDir + "/lib"; - if (directories.contains(directory)) { - continue; - } - String permissions[] = pkgInfo.requestedPermissions; - if (permissions == null) { - continue; - } - boolean permissionOk = false; - for (String permit : permissions) { - if (PLUGIN_PERMISSION.equals(permit)) { - permissionOk = true; + + synchronized(mPackageInfoCache) { + + // clear the list of existing packageInfo objects + mPackageInfoCache.clear(); + + for (ResolveInfo info : plugins) { + ServiceInfo serviceInfo = info.serviceInfo; + if (serviceInfo == null) { + Log.w(LOGTAG, "Ignore bad plugin"); + continue; + } + PackageInfo pkgInfo; + try { + pkgInfo = pm.getPackageInfo(serviceInfo.packageName, + PackageManager.GET_PERMISSIONS + | PackageManager.GET_SIGNATURES); + } catch (NameNotFoundException e) { + Log.w(LOGTAG, "Cant find plugin: " + serviceInfo.packageName); + continue; + } + if (pkgInfo == null) { + continue; + } + String directory = pkgInfo.applicationInfo.dataDir + "/lib"; + if (directories.contains(directory)) { + continue; + } + String permissions[] = pkgInfo.requestedPermissions; + if (permissions == null) { + continue; + } + boolean permissionOk = false; + for (String permit : permissions) { + if (PLUGIN_PERMISSION.equals(permit)) { + permissionOk = true; + break; + } + } + if (!permissionOk) { + continue; + } + Signature signatures[] = pkgInfo.signatures; + if (signatures == null) { + continue; + } + boolean signatureMatch = false; + for (Signature signature : signatures) { + // TODO: check signature against Google provided one + signatureMatch = true; break; } + if (!signatureMatch) { + continue; + } + mPackageInfoCache.add(pkgInfo); + directories.add(directory); } - if (!permissionOk) { - continue; - } - Signature signatures[] = pkgInfo.signatures; - if (signatures == null) { - continue; - } - boolean signatureMatch = false; - for (Signature signature : signatures) { - // TODO: check signature against Google provided one - signatureMatch = true; - break; - } - if (!signatureMatch) { - continue; - } - directories.add(directory); } return directories.toArray(new String[directories.size()]); } + String getPluginsAPKName(String pluginLib) { + + // basic error checking on input params + if (pluginLib == null || pluginLib.length() == 0) { + return null; + } + + // must be synchronized to ensure the consistency of the cache + synchronized(mPackageInfoCache) { + for (PackageInfo pkgInfo : mPackageInfoCache) { + if (pluginLib.startsWith(pkgInfo.applicationInfo.dataDir)) { + return pkgInfo.packageName; + } + } + } + + // if no apk was found then return null + return null; + } + String getPluginSharedDataDirectory() { return mContext.getDir("plugins", 0).getPath(); } diff --git a/core/java/android/webkit/ValueCallback.java b/core/java/android/webkit/ValueCallback.java new file mode 100644 index 0000000..d8c5cdc --- /dev/null +++ b/core/java/android/webkit/ValueCallback.java @@ -0,0 +1,29 @@ +/* + * 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.webkit; + +/** + * A callback interface used to returns values asynchronously + * + * @hide pending council approval + */ +public interface ValueCallback<T> { + /** + * Invoked when we have the result + */ + public void onReceiveValue(T value); +}; diff --git a/core/java/android/webkit/WebChromeClient.java b/core/java/android/webkit/WebChromeClient.java index 0e08514..1ae1d85 100644 --- a/core/java/android/webkit/WebChromeClient.java +++ b/core/java/android/webkit/WebChromeClient.java @@ -48,9 +48,11 @@ public class WebChromeClient { * Notify the host application of the url for an apple-touch-icon. * @param view The WebView that initiated the callback. * @param url The icon url. + * @param precomposed True if the url is for a precomposed touch icon. * @hide pending council approval */ - public void onReceivedTouchIconUrl(WebView view, String url) {} + public void onReceivedTouchIconUrl(WebView view, String url, + boolean precomposed) {} /** * A callback interface used by the host application to notify diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java index 3c43fd1..0cfcb95 100644 --- a/core/java/android/webkit/WebSettings.java +++ b/core/java/android/webkit/WebSettings.java @@ -190,12 +190,8 @@ public class WebSettings { private boolean mAllowFileAccess = true; private boolean mLoadWithOverviewMode = false; - // Manages interaction of the system setting 'Location & security - Share - // with Google' and the browser. - static GoogleLocationSettingManager sGoogleLocationSettingManager; - // private WebSettings, not accessible by the host activity - private int mDoubleTapToastCount = 3; + static private int mDoubleTapToastCount = 3; private static final String PREF_FILE = "WebViewSettings"; private static final String DOUBLE_TAP_TOAST_COUNT = "double_tap_toast_count"; @@ -1353,17 +1349,28 @@ public class WebSettings { if (DebugFlags.WEB_SETTINGS) { junit.framework.Assert.assertTrue(frame.mNativeFrame != 0); } - sGoogleLocationSettingManager = new GoogleLocationSettingManager(mContext); - sGoogleLocationSettingManager.start(); + + GoogleLocationSettingManager.getInstance().start(mContext); + SharedPreferences sp = mContext.getSharedPreferences(PREF_FILE, Context.MODE_PRIVATE); - mDoubleTapToastCount = sp.getInt(DOUBLE_TAP_TOAST_COUNT, - mDoubleTapToastCount); + if (mDoubleTapToastCount > 0) { + mDoubleTapToastCount = sp.getInt(DOUBLE_TAP_TOAST_COUNT, + mDoubleTapToastCount); + } nativeSync(frame.mNativeFrame); mSyncPending = false; mEventHandler.createHandler(); } + /** + * Let the Settings object know that our owner is being destroyed. + */ + /*package*/ + synchronized void onDestroyed() { + GoogleLocationSettingManager.getInstance().stop(); + } + private int pin(int size) { // FIXME: 72 is just an arbitrary max text size value. if (size < 1) { diff --git a/core/java/android/webkit/WebStorage.java b/core/java/android/webkit/WebStorage.java index ae560fb..0022248 100644 --- a/core/java/android/webkit/WebStorage.java +++ b/core/java/android/webkit/WebStorage.java @@ -20,9 +20,8 @@ import android.os.Handler; import android.os.Message; import android.util.Log; -import java.util.concurrent.locks.Condition; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReentrantLock; +import java.util.Collection; +import java.util.Map; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; @@ -51,28 +50,41 @@ public final class WebStorage { // Global instance of a WebStorage private static WebStorage sWebStorage; - // We keep the origins, quotas and usages as member values - // that we protect via a lock and update in syncValues(). - // This is needed to transfer this data across threads. - private static Lock mLock = new ReentrantLock(); - private static Condition mUpdateCondition = mLock.newCondition(); - private static boolean mUpdateAvailable; - // Message ids static final int UPDATE = 0; static final int SET_QUOTA_ORIGIN = 1; static final int DELETE_ORIGIN = 2; static final int DELETE_ALL = 3; + static final int GET_ORIGINS = 4; + static final int GET_USAGE_ORIGIN = 5; + static final int GET_QUOTA_ORIGIN = 6; + + // Message ids on the UI thread + static final int RETURN_ORIGINS = 0; + static final int RETURN_USAGE_ORIGIN = 1; + static final int RETURN_QUOTA_ORIGIN = 2; + + private static final String ORIGINS = "origins"; + private static final String ORIGIN = "origin"; + private static final String CALLBACK = "callback"; + private static final String USAGE = "usage"; + private static final String QUOTA = "quota"; - private Set <String> mOrigins; - private HashMap <String, Long> mQuotas = new HashMap<String, Long>(); - private HashMap <String, Long> mUsages = new HashMap<String, Long>(); + private Map <String, Origin> mOrigins; private Handler mHandler = null; + private Handler mUIHandler = null; - private static class Origin { + static class Origin { String mOrigin = null; long mQuota = 0; + long mUsage = 0; + + public Origin(String origin, long quota, long usage) { + mOrigin = origin; + mQuota = quota; + mUsage = usage; + } public Origin(String origin, long quota) { mOrigin = origin; @@ -90,11 +102,49 @@ public final class WebStorage { public long getQuota() { return mQuota; } + + public long getUsage() { + return mUsage; + } + } + + /** + * @hide + * Message handler, UI side + */ + public void createUIHandler() { + if (mUIHandler == null) { + mUIHandler = new Handler() { + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case RETURN_ORIGINS: { + Map values = (Map) msg.obj; + Map origins = (Map) values.get(ORIGINS); + ValueCallback<Map> callback = (ValueCallback<Map>) values.get(CALLBACK); + callback.onReceiveValue(origins); + } break; + + case RETURN_USAGE_ORIGIN: { + Map values = (Map) msg.obj; + ValueCallback<Long> callback = (ValueCallback<Long>) values.get(CALLBACK); + callback.onReceiveValue((Long)values.get(USAGE)); + } break; + + case RETURN_QUOTA_ORIGIN: { + Map values = (Map) msg.obj; + ValueCallback<Long> callback = (ValueCallback<Long>) values.get(CALLBACK); + callback.onReceiveValue((Long)values.get(QUOTA)); + } break; + } + } + }; + } } /** * @hide - * Message handler + * Message handler, webcore side */ public void createHandler() { if (mHandler == null) { @@ -117,6 +167,46 @@ public final class WebStorage { nativeDeleteAllData(); break; + case GET_ORIGINS: { + syncValues(); + ValueCallback callback = (ValueCallback) msg.obj; + Map origins = new HashMap(mOrigins); + Map values = new HashMap<String, Object>(); + values.put(CALLBACK, callback); + values.put(ORIGINS, origins); + postUIMessage(Message.obtain(null, RETURN_ORIGINS, values)); + } break; + + case GET_USAGE_ORIGIN: { + syncValues(); + Map values = (Map) msg.obj; + String origin = (String) values.get(ORIGIN); + ValueCallback callback = (ValueCallback) values.get(CALLBACK); + Origin website = mOrigins.get(origin); + Map retValues = new HashMap<String, Object>(); + retValues.put(CALLBACK, callback); + if (website != null) { + long usage = website.getUsage(); + retValues.put(USAGE, new Long(usage)); + } + postUIMessage(Message.obtain(null, RETURN_USAGE_ORIGIN, retValues)); + } break; + + case GET_QUOTA_ORIGIN: { + syncValues(); + Map values = (Map) msg.obj; + String origin = (String) values.get(ORIGIN); + ValueCallback callback = (ValueCallback) values.get(CALLBACK); + Origin website = mOrigins.get(origin); + Map retValues = new HashMap<String, Object>(); + retValues.put(CALLBACK, callback); + if (website != null) { + long quota = website.getQuota(); + retValues.put(QUOTA, new Long(quota)); + } + postUIMessage(Message.obtain(null, RETURN_QUOTA_ORIGIN, retValues)); + } break; + case UPDATE: syncValues(); break; @@ -126,82 +216,91 @@ public final class WebStorage { } } + /* + * When calling getOrigins(), getUsageForOrigin() and getQuotaForOrigin(), + * we need to get the values from webcore, but we cannot block while doing so + * as we used to do, as this could result in a full deadlock (other webcore + * messages received while we are still blocked here, see http://b/2127737). + * + * We have to do everything asynchronously, by providing a callback function. + * We post a message on the webcore thread (mHandler) that will get the result + * from webcore, and we post it back on the UI thread (using mUIHandler). + * We can then use the callback function to return the value. + */ + /** * @hide * Returns a list of origins having a database */ - public Set getOrigins() { - Set ret = null; - mLock.lock(); - try { - mUpdateAvailable = false; - update(); - while (!mUpdateAvailable) { - mUpdateCondition.await(); + public void getOrigins(ValueCallback<Map> callback) { + if (callback != null) { + if (WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName())) { + syncValues(); + callback.onReceiveValue(mOrigins); + } else { + postMessage(Message.obtain(null, GET_ORIGINS, callback)); } - ret = mOrigins; - } catch (InterruptedException e) { - Log.e(TAG, "Exception while waiting on the updated origins", e); - } finally { - mLock.unlock(); } - return ret; + } + + /** + * Returns a list of origins having a database + * should only be called from WebViewCore. + */ + Collection<Origin> getOriginsSync() { + if (WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName())) { + update(); + return mOrigins.values(); + } + return null; } /** * @hide * Returns the use for a given origin */ - public long getUsageForOrigin(String origin) { - long ret = 0; + public void getUsageForOrigin(String origin, ValueCallback<Long> callback) { + if (callback == null) { + return; + } if (origin == null) { - return ret; + callback.onReceiveValue(null); + return; } - mLock.lock(); - try { - mUpdateAvailable = false; - update(); - while (!mUpdateAvailable) { - mUpdateCondition.await(); - } - Long usage = mUsages.get(origin); - if (usage != null) { - ret = usage.longValue(); - } - } catch (InterruptedException e) { - Log.e(TAG, "Exception while waiting on the updated origins", e); - } finally { - mLock.unlock(); + if (WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName())) { + syncValues(); + Origin website = mOrigins.get(origin); + callback.onReceiveValue(new Long(website.getUsage())); + } else { + HashMap values = new HashMap<String, Object>(); + values.put(ORIGIN, origin); + values.put(CALLBACK, callback); + postMessage(Message.obtain(null, GET_USAGE_ORIGIN, values)); } - return ret; } /** * @hide * Returns the quota for a given origin */ - public long getQuotaForOrigin(String origin) { - long ret = 0; + public void getQuotaForOrigin(String origin, ValueCallback<Long> callback) { + if (callback == null) { + return; + } if (origin == null) { - return ret; + callback.onReceiveValue(null); + return; } - mLock.lock(); - try { - mUpdateAvailable = false; - update(); - while (!mUpdateAvailable) { - mUpdateCondition.await(); - } - Long quota = mQuotas.get(origin); - if (quota != null) { - ret = quota.longValue(); - } - } catch (InterruptedException e) { - Log.e(TAG, "Exception while waiting on the updated origins", e); - } finally { - mLock.unlock(); + if (WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName())) { + syncValues(); + Origin website = mOrigins.get(origin); + callback.onReceiveValue(new Long(website.getUsage())); + } else { + HashMap values = new HashMap<String, Object>(); + values.put(ORIGIN, origin); + values.put(CALLBACK, callback); + postMessage(Message.obtain(null, GET_QUOTA_ORIGIN, values)); } - return ret; } /** @@ -256,6 +355,15 @@ public final class WebStorage { } /** + * Utility function to send a message to the handler on the UI thread + */ + private void postUIMessage(Message msg) { + if (mUIHandler != null) { + mUIHandler.sendMessage(msg); + } + } + + /** * @hide * Get the global instance of WebStorage. * @return A single instance of WebStorage. @@ -284,21 +392,14 @@ public final class WebStorage { * set the local values with the current ones */ private void syncValues() { - mLock.lock(); - Set tmp = nativeGetOrigins(); - mOrigins = new HashSet<String>(); - mQuotas.clear(); - mUsages.clear(); - Iterator<String> iter = tmp.iterator(); - while (iter.hasNext()) { - String origin = iter.next(); - mOrigins.add(origin); - mQuotas.put(origin, new Long(nativeGetQuotaForOrigin(origin))); - mUsages.put(origin, new Long(nativeGetUsageForOrigin(origin))); + Set<String> tmp = nativeGetOrigins(); + mOrigins = new HashMap<String, Origin>(); + for (String origin : tmp) { + Origin website = new Origin(origin, + nativeGetUsageForOrigin(origin), + nativeGetQuotaForOrigin(origin)); + mOrigins.put(origin, website); } - mUpdateAvailable = true; - mUpdateCondition.signal(); - mLock.unlock(); } // Native functions diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java index 113eac5..51c5e1f 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -3772,10 +3772,10 @@ public class WebView extends AbsoluteLayout mZoomButtonsController.setVisible(true); int count = settings.getDoubleTapToastCount(); if (mInZoomOverview && count > 0) { - settings.setDoubleTapToastCount(count--); + settings.setDoubleTapToastCount(--count); Toast.makeText(mContext, com.android.internal.R.string.double_tap_toast, - Toast.LENGTH_SHORT).show(); + Toast.LENGTH_LONG).show(); } } } diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java index e734444..ce45373 100644 --- a/core/java/android/webkit/WebViewCore.java +++ b/core/java/android/webkit/WebViewCore.java @@ -17,6 +17,7 @@ package android.webkit; import android.content.Context; +import android.content.Intent; import android.graphics.Canvas; import android.graphics.DrawFilter; import android.graphics.Paint; @@ -37,6 +38,7 @@ import android.view.SurfaceView; import android.view.View; import java.util.ArrayList; +import java.util.Collection; import java.util.Map; import java.util.Set; @@ -162,8 +164,10 @@ final class WebViewCore { // The WebIconDatabase needs to be initialized within the UI thread so // just request the instance here. WebIconDatabase.getInstance(); - // Create the WebStorage singleton - WebStorage.getInstance(); + // Create the WebStorage singleton and the UI handler + WebStorage.getInstance().createUIHandler(); + // Create the UI handler for GeolocationPermissions + GeolocationPermissions.getInstance().createUIHandler(); // Send a message to initialize the WebViewCore. Message init = sWebCoreHandler.obtainMessage( WebCoreThread.INITIALIZE, this); @@ -850,6 +854,7 @@ final class WebViewCore { synchronized (WebViewCore.this) { mBrowserFrame.destroy(); mBrowserFrame = null; + mSettings.onDestroyed(); mNativeClass = 0; } break; @@ -1519,13 +1524,14 @@ final class WebViewCore { // callbacks. Computes the sum of database quota for all origins. private long getUsedQuota() { WebStorage webStorage = WebStorage.getInstance(); - Set<String> origins = webStorage.getOrigins(); + Collection<WebStorage.Origin> origins = webStorage.getOriginsSync(); + if (origins == null) { return 0; } long usedQuota = 0; - for (String origin : origins) { - usedQuota += webStorage.getQuotaForOrigin(origin); + for (WebStorage.Origin website : origins) { + usedQuota += website.getQuota(); } return usedQuota; } @@ -2086,22 +2092,49 @@ final class WebViewCore { } } - // PluginWidget functions for creating SurfaceViews for the Surface drawing - // model. - private ViewManager.ChildView createSurface(String packageName, String className, + // called by JNI. PluginWidget function to launch an activity and overlays + // the activity with the View provided by the plugin class. + private void startFullScreenPluginActivity(String libName, String clsName, int npp) { + if (mWebView == null) { + return; + } + + String pkgName = PluginManager.getInstance(null).getPluginsAPKName(libName); + if (pkgName == null) { + Log.w(LOGTAG, "Unable to resolve " + libName + " to a plugin APK"); + return; + } + + Intent intent = new Intent("android.intent.webkit.PLUGIN"); + intent.putExtra(PluginActivity.INTENT_EXTRA_PACKAGE_NAME, pkgName); + intent.putExtra(PluginActivity.INTENT_EXTRA_CLASS_NAME, clsName); + intent.putExtra(PluginActivity.INTENT_EXTRA_NPP_INSTANCE, npp); + mWebView.getContext().startActivity(intent); + } + + // called by JNI. PluginWidget functions for creating an embedded View for + // the surface drawing model. + private ViewManager.ChildView createSurface(String libName, String clsName, int npp, int x, int y, int width, int height) { if (mWebView == null) { return null; } - PluginStub stub = PluginUtil.getPluginStub(mWebView.getContext(), packageName, className); + + String pkgName = PluginManager.getInstance(null).getPluginsAPKName(libName); + if (pkgName == null) { + Log.w(LOGTAG, "Unable to resolve " + libName + " to a plugin APK"); + return null; + } + + PluginStub stub =PluginUtil.getPluginStub(mWebView.getContext(),pkgName, clsName); if (stub == null) { - Log.e(LOGTAG, "Unable to find plugin class (" + className + - ") in the apk (" + packageName + ")"); + Log.e(LOGTAG, "Unable to find plugin class (" + clsName + + ") in the apk (" + pkgName + ")"); return null; } - + View pluginView = stub.getEmbeddedView(npp, mWebView.getContext()); - + ViewManager.ChildView view = mWebView.mViewManager.createView(); view.mView = pluginView; view.attachView(x, y, width, height); diff --git a/core/java/android/widget/ScrollView.java b/core/java/android/widget/ScrollView.java index 703cd8e..31c7814 100644 --- a/core/java/android/widget/ScrollView.java +++ b/core/java/android/widget/ScrollView.java @@ -743,7 +743,7 @@ public class ScrollView extends FrameLayout { final int maxJump = getMaxScrollAmount(); - if (nextFocused != null && isWithinDeltaOfScreen(nextFocused, maxJump)) { + if (nextFocused != null && isWithinDeltaOfScreen(nextFocused, maxJump, getHeight())) { nextFocused.getDrawingRect(mTempRect); offsetDescendantRectToMyCoords(nextFocused, mTempRect); int scrollDelta = computeScrollDeltaToGetChildRectOnScreen(mTempRect); @@ -792,19 +792,19 @@ public class ScrollView extends FrameLayout { * screen. */ private boolean isOffScreen(View descendant) { - return !isWithinDeltaOfScreen(descendant, 0); + return !isWithinDeltaOfScreen(descendant, 0, getHeight()); } /** * @return whether the descendant of this scroll view is within delta * pixels of being on the screen. */ - private boolean isWithinDeltaOfScreen(View descendant, int delta) { + private boolean isWithinDeltaOfScreen(View descendant, int delta, int height) { descendant.getDrawingRect(mTempRect); offsetDescendantRectToMyCoords(descendant, mTempRect); return (mTempRect.bottom + delta) >= getScrollY() - && (mTempRect.top - delta) <= (getScrollY() + getHeight()); + && (mTempRect.top - delta) <= (getScrollY() + height); } /** @@ -1124,9 +1124,10 @@ public class ScrollView extends FrameLayout { if (null == currentFocused || this == currentFocused) return; - final int maxJump = mBottom - mTop; - - if (isWithinDeltaOfScreen(currentFocused, maxJump)) { + // If the currently-focused view was visible on the screen when the + // screen was at the old height, then scroll the screen to make that + // view visible with the new screen height. + if (isWithinDeltaOfScreen(currentFocused, 0, oldh)) { currentFocused.getDrawingRect(mTempRect); offsetDescendantRectToMyCoords(currentFocused, mTempRect); int scrollDelta = computeScrollDeltaToGetChildRectOnScreen(mTempRect); diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 2e3364b..3c61c1c 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -2874,26 +2874,23 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * @attr ref android.R.styleable#TextView_inputType */ public void setInputType(int type) { + final boolean wasPassword = isPasswordInputType(mInputType); + final boolean wasVisiblePassword = isVisiblePasswordInputType(mInputType); setInputType(type, false); - final int variation = type&(EditorInfo.TYPE_MASK_CLASS - |EditorInfo.TYPE_MASK_VARIATION); - final boolean isPassword = variation - == (EditorInfo.TYPE_CLASS_TEXT - |EditorInfo.TYPE_TEXT_VARIATION_PASSWORD); + final boolean isPassword = isPasswordInputType(type); + final boolean isVisiblePassword = isVisiblePasswordInputType(type); boolean forceUpdate = false; if (isPassword) { setTransformationMethod(PasswordTransformationMethod.getInstance()); setTypefaceByIndex(MONOSPACE, 0); - } else if (mTransformation == PasswordTransformationMethod.getInstance()) { - // We need to clean up if we were previously in password mode. - if (variation != (EditorInfo.TYPE_CLASS_TEXT - |EditorInfo.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD)) { - setTypefaceByIndex(-1, -1); - } - forceUpdate = true; - } else if (variation == (EditorInfo.TYPE_CLASS_TEXT - |EditorInfo.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD)) { + } else if (isVisiblePassword) { setTypefaceByIndex(MONOSPACE, 0); + } else if (wasPassword || wasVisiblePassword) { + // not in password mode, clean up typeface and transformation + setTypefaceByIndex(-1, -1); + if (mTransformation == PasswordTransformationMethod.getInstance()) { + forceUpdate = true; + } } boolean multiLine = (type&(EditorInfo.TYPE_MASK_CLASS @@ -2913,6 +2910,22 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (imm != null) imm.restartInput(this); } + private boolean isPasswordInputType(int inputType) { + final int variation = inputType & (EditorInfo.TYPE_MASK_CLASS + | EditorInfo.TYPE_MASK_VARIATION); + return variation + == (EditorInfo.TYPE_CLASS_TEXT + | EditorInfo.TYPE_TEXT_VARIATION_PASSWORD); + } + + private boolean isVisiblePasswordInputType(int inputType) { + final int variation = inputType & (EditorInfo.TYPE_MASK_CLASS + | EditorInfo.TYPE_MASK_VARIATION); + return variation + == (EditorInfo.TYPE_CLASS_TEXT + | EditorInfo.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD); + } + /** * Directly change the content type integer of the text view, without * modifying any other state. diff --git a/core/java/com/android/internal/widget/RotarySelector.java b/core/java/com/android/internal/widget/RotarySelector.java index f610211..426cef5 100644 --- a/core/java/com/android/internal/widget/RotarySelector.java +++ b/core/java/com/android/internal/widget/RotarySelector.java @@ -25,8 +25,9 @@ import android.util.AttributeSet; import android.util.Log; import android.view.MotionEvent; import android.view.View; -import android.view.SoundEffectConstants; -import android.view.animation.AccelerateInterpolator; +import android.view.VelocityTracker; +import android.view.ViewConfiguration; +import android.view.animation.DecelerateInterpolator; import static android.view.animation.AnimationUtils.currentAnimationTimeMillis; import com.android.internal.R; @@ -60,16 +61,18 @@ public class RotarySelector extends View { private int mLeftHandleX; private int mRightHandleX; - // current offset of user's dragging - private int mTouchDragOffset = 0; + // current offset of rotary widget along the x axis + private int mRotaryOffsetX = 0; // state of the animation used to bring the handle back to its start position when // the user lets go before triggering an action private boolean mAnimating = false; - private long mAnimationStartTime; // set to the end point of the animatino + private long mAnimationStartTime; private long mAnimationDuration; - private int mAnimatingDeltaXStart; // the animation will interpolate from this delta down to zero - private AccelerateInterpolator mInterpolator; + private int mAnimatingDeltaXStart; // the animation will interpolate from this delta to zero + private int mAnimatingDeltaXEnd; + + private DecelerateInterpolator mInterpolator; /** * If the user is currently dragging something. @@ -87,8 +90,8 @@ public class RotarySelector extends View { // Vibration (haptic feedback) private Vibrator mVibrator; - private static final long VIBRATE_SHORT = 60; // msec - private static final long VIBRATE_LONG = 100; // msec + private static final long VIBRATE_SHORT = 30; // msec + private static final long VIBRATE_LONG = 60; // msec /** * The drawable for the arrows need to be scrunched this many dips towards the rotary bg below @@ -114,11 +117,27 @@ public class RotarySelector extends View { static final int SNAP_BACK_ANIMATION_DURATION_MILLIS = 300; static final int SPIN_ANIMATION_DURATION_MILLIS = 800; - private static final boolean DRAW_CENTER_DIMPLE = false; + private static final boolean DRAW_CENTER_DIMPLE = true; private int mEdgeTriggerThresh; private int mDimpleWidth; private int mBackgroundWidth; private int mBackgroundHeight; + private final int mOuterRadius; + private final int mInnerRadius; + private int mDimpleSpacing; + + private VelocityTracker mVelocityTracker; + private int mMinimumVelocity; + private int mMaximumVelocity; + + /** + * The number of dimples we are flinging when we do the "spin" animation. Used to know when to + * wrap the icons back around so they "rotate back" onto the screen. + * @see #updateAnimation() + */ + private int mDimplesOfFling = 0; + + public RotarySelector(Context context) { this(context, null); @@ -152,7 +171,7 @@ public class RotarySelector extends View { mArrowLongLeft.setBounds(0, 0, arrowW, arrowH); mArrowLongRight.setBounds(0, 0, arrowW, arrowH); - mInterpolator = new AccelerateInterpolator(); + mInterpolator = new DecelerateInterpolator(1f); mEdgeTriggerThresh = (int) (mDensity * EDGE_TRIGGER_DIP); @@ -160,6 +179,23 @@ public class RotarySelector extends View { mBackgroundWidth = mBackground.getIntrinsicWidth(); mBackgroundHeight = mBackground.getIntrinsicHeight(); + mOuterRadius = (int) (mDensity * OUTER_ROTARY_RADIUS_DIP); + mInnerRadius = (int) ((OUTER_ROTARY_RADIUS_DIP - ROTARY_STROKE_WIDTH_DIP) * mDensity); + + final ViewConfiguration configuration = ViewConfiguration.get(mContext); + mMinimumVelocity = configuration.getScaledMinimumFlingVelocity() * 2; + mMaximumVelocity = configuration.getScaledMaximumFlingVelocity(); + } + + @Override + protected void onSizeChanged(int w, int h, int oldw, int oldh) { + super.onSizeChanged(w, h, oldw, oldh); + + mLeftHandleX = (int) (EDGE_PADDING_DIP * mDensity) + mDimpleWidth / 2; + mRightHandleX = + getWidth() - (int) (EDGE_PADDING_DIP * mDensity) - mDimpleWidth / 2; + + mDimpleSpacing = (getWidth() / 2) - mLeftHandleX; } /** @@ -229,43 +265,21 @@ public class RotarySelector extends View { setMeasuredDimension(width, backgroundH + arrowH - arrowScrunch); } - @Override - protected void onSizeChanged(int w, int h, int oldw, int oldh) { - super.onSizeChanged(w, h, oldw, oldh); - - mLeftHandleX = (int) (EDGE_PADDING_DIP * mDensity) + mDimpleWidth / 2; - mRightHandleX = - getWidth() - (int) (EDGE_PADDING_DIP * mDensity) - mDimpleWidth / 2; - } - // private Paint mPaint = new Paint(); @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); if (DBG) { - log(String.format("onDraw: mAnimating=%s, mTouchDragOffset=%d, mGrabbedState=%d", - mAnimating, mTouchDragOffset, mGrabbedState)); + log(String.format("onDraw: mAnimating=%s, mRotaryOffsetX=%d, mGrabbedState=%d", + mAnimating, mRotaryOffsetX, mGrabbedState)); } final int height = getHeight(); // update animating state before we draw anything if (mAnimating) { - final long millisSoFar = currentAnimationTimeMillis() - mAnimationStartTime; - final long millisLeft = mAnimationDuration - millisSoFar; - if (DBG) log("millisleft for animating: " + millisLeft); - if (millisLeft <= 0) { - reset(); - } else { - // we always use the snap back duration as the denominator for interpolation - // to get a consistent velocity (bascially this makes us happy for the snap back - // and the spin around one). - final long denom = SNAP_BACK_ANIMATION_DURATION_MILLIS; // mAnimationDuration - float interpolation = mInterpolator.getInterpolation( - (float) millisLeft / denom); - mTouchDragOffset = (int) (mAnimatingDeltaXStart * interpolation); - } + updateAnimation(); } // Background: @@ -302,16 +316,13 @@ public class RotarySelector extends View { // float or = OUTER_ROTARY_RADIUS_DIP * mDensity; // canvas.drawCircle(getWidth() / 2, or + mBackground.getBounds().top, or, mPaint); - final int outerRadius = (int) (mDensity * OUTER_ROTARY_RADIUS_DIP); - final int innerRadius = - (int) ((OUTER_ROTARY_RADIUS_DIP - ROTARY_STROKE_WIDTH_DIP) * mDensity); final int bgTop = mBackground.getBounds().top; { - final int xOffset = mLeftHandleX + mTouchDragOffset; + final int xOffset = mLeftHandleX + mRotaryOffsetX; final int drawableY = getYOnArc( mBackground, - innerRadius, - outerRadius, + mInnerRadius, + mOuterRadius, xOffset); drawCentered(mDimple, canvas, xOffset, drawableY + bgTop); @@ -321,22 +332,22 @@ public class RotarySelector extends View { } if (DRAW_CENTER_DIMPLE) { - final int xOffset = getWidth() / 2 + mTouchDragOffset; + final int xOffset = getWidth() / 2 + mRotaryOffsetX; final int drawableY = getYOnArc( mBackground, - innerRadius, - outerRadius, + mInnerRadius, + mOuterRadius, xOffset); drawCentered(mDimple, canvas, xOffset, drawableY + bgTop); } { - final int xOffset = mRightHandleX + mTouchDragOffset; + final int xOffset = mRightHandleX + mRotaryOffsetX; final int drawableY = getYOnArc( mBackground, - innerRadius, - outerRadius, + mInnerRadius, + mOuterRadius, xOffset); drawCentered(mDimple, canvas, xOffset, drawableY + bgTop); @@ -345,7 +356,33 @@ public class RotarySelector extends View { } } - if (mAnimating) invalidate(); + // draw extra left hand dimples + int dimpleLeft = mRotaryOffsetX + mLeftHandleX - mDimpleSpacing; + final int halfdimple = mDimpleWidth / 2; + while (dimpleLeft > -halfdimple) { + final int drawableY = getYOnArc( + mBackground, + mInnerRadius, + mOuterRadius, + dimpleLeft); + + drawCentered(mDimple, canvas, dimpleLeft, drawableY + bgTop); + dimpleLeft -= mDimpleSpacing; + } + + // draw extra right hand dimples + int dimpleRight = mRotaryOffsetX + mRightHandleX + mDimpleSpacing; + final int rightThresh = mRight + halfdimple; + while (dimpleRight < rightThresh) { + final int drawableY = getYOnArc( + mBackground, + mInnerRadius, + mOuterRadius, + dimpleRight); + + drawCentered(mDimple, canvas, dimpleRight, drawableY + bgTop); + dimpleRight += mDimpleSpacing; + } } /** @@ -395,6 +432,11 @@ public class RotarySelector extends View { if (mAnimating) { return true; } + if (mVelocityTracker == null) { + mVelocityTracker = VelocityTracker.obtain(); + } + mVelocityTracker.addMovement(event); + final int eventX = (int) event.getX(); final int hitWindow = mDimpleWidth; @@ -409,12 +451,12 @@ public class RotarySelector extends View { invalidate(); } if (eventX < mLeftHandleX + hitWindow) { - mTouchDragOffset = eventX - mLeftHandleX; + mRotaryOffsetX = eventX - mLeftHandleX; mGrabbedState = LEFT_HANDLE_GRABBED; invalidate(); vibrate(VIBRATE_SHORT); } else if (eventX > mRightHandleX - hitWindow) { - mTouchDragOffset = eventX - mRightHandleX; + mRotaryOffsetX = eventX - mRightHandleX; mGrabbedState = RIGHT_HANDLE_GRABBED; invalidate(); vibrate(VIBRATE_SHORT); @@ -424,35 +466,38 @@ public class RotarySelector extends View { case MotionEvent.ACTION_MOVE: if (DBG) log("touch-move"); if (mGrabbedState == LEFT_HANDLE_GRABBED) { - mTouchDragOffset = eventX - mLeftHandleX; + mRotaryOffsetX = eventX - mLeftHandleX; invalidate(); if (eventX >= getRight() - mEdgeTriggerThresh && !mTriggered) { mTriggered = true; dispatchTriggerEvent(OnDialTriggerListener.LEFT_HANDLE); - // set up "spin around animation" - mAnimating = true; - mAnimationStartTime = currentAnimationTimeMillis(); - mAnimationDuration = SPIN_ANIMATION_DURATION_MILLIS; - mAnimatingDeltaXStart = -mBackgroundWidth*3; - mTouchDragOffset = 0; - mGrabbedState = NOTHING_GRABBED; - invalidate(); - + final VelocityTracker velocityTracker = mVelocityTracker; + velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity); + final int velocity = Math.max(mMinimumVelocity, (int) velocityTracker.getXVelocity()); + mDimplesOfFling = Math.max( + 8, + Math.abs(velocity / mDimpleSpacing)); + startAnimationWithVelocity( + eventX - mLeftHandleX, + mDimplesOfFling * mDimpleSpacing, + velocity); } } else if (mGrabbedState == RIGHT_HANDLE_GRABBED) { - mTouchDragOffset = eventX - mRightHandleX; + mRotaryOffsetX = eventX - mRightHandleX; invalidate(); if (eventX <= mEdgeTriggerThresh && !mTriggered) { mTriggered = true; dispatchTriggerEvent(OnDialTriggerListener.RIGHT_HANDLE); - // set up "spin around animation" - mAnimating = true; - mAnimationStartTime = currentAnimationTimeMillis(); - mAnimationDuration = SPIN_ANIMATION_DURATION_MILLIS; - mAnimatingDeltaXStart = mBackgroundWidth*3; - mTouchDragOffset = 0; - mGrabbedState = NOTHING_GRABBED; - invalidate(); + final VelocityTracker velocityTracker = mVelocityTracker; + velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity); + final int velocity = Math.min(-mMinimumVelocity, (int) velocityTracker.getXVelocity()); + mDimplesOfFling = Math.max( + 8, + Math.abs(velocity / mDimpleSpacing)); + startAnimationWithVelocity( + eventX - mRightHandleX, + -(mDimplesOfFling * mDimpleSpacing), + velocity); } } break; @@ -462,35 +507,84 @@ public class RotarySelector extends View { if (mGrabbedState == LEFT_HANDLE_GRABBED && Math.abs(eventX - mLeftHandleX) > 5) { // set up "snap back" animation - mAnimating = true; - mAnimationStartTime = currentAnimationTimeMillis(); - mAnimationDuration = SNAP_BACK_ANIMATION_DURATION_MILLIS; - mAnimatingDeltaXStart = eventX - mLeftHandleX; + startAnimation(eventX - mLeftHandleX, 0, SNAP_BACK_ANIMATION_DURATION_MILLIS); } else if (mGrabbedState == RIGHT_HANDLE_GRABBED && Math.abs(eventX - mRightHandleX) > 5) { // set up "snap back" animation - mAnimating = true; - mAnimationStartTime = currentAnimationTimeMillis(); - mAnimationDuration = SNAP_BACK_ANIMATION_DURATION_MILLIS; - mAnimatingDeltaXStart = eventX - mRightHandleX; + startAnimation(eventX - mRightHandleX, 0, SNAP_BACK_ANIMATION_DURATION_MILLIS); } - - mTouchDragOffset = 0; + mRotaryOffsetX = 0; mGrabbedState = NOTHING_GRABBED; invalidate(); + if (mVelocityTracker != null) { + mVelocityTracker.recycle(); // wishin' we had generational GC + mVelocityTracker = null; + } break; case MotionEvent.ACTION_CANCEL: if (DBG) log("touch-cancel"); reset(); invalidate(); + if (mVelocityTracker != null) { + mVelocityTracker.recycle(); + mVelocityTracker = null; + } break; } return true; } + private void startAnimation(int startX, int endX, int duration) { + mAnimating = true; + mAnimationStartTime = currentAnimationTimeMillis(); + mAnimationDuration = duration; + mAnimatingDeltaXStart = startX; + mAnimatingDeltaXEnd = endX; + mGrabbedState = NOTHING_GRABBED; + mDimplesOfFling = 0; + invalidate(); + } + + private void startAnimationWithVelocity(int startX, int endX, int pixelsPerSecond) { + mAnimating = true; + mAnimationStartTime = currentAnimationTimeMillis(); + mAnimationDuration = 1000 * (endX - startX) / pixelsPerSecond; + mAnimatingDeltaXStart = startX; + mAnimatingDeltaXEnd = endX; + mGrabbedState = NOTHING_GRABBED; + invalidate(); + } + + private void updateAnimation() { + final long millisSoFar = currentAnimationTimeMillis() - mAnimationStartTime; + final long millisLeft = mAnimationDuration - millisSoFar; + final int totalDeltaX = mAnimatingDeltaXStart - mAnimatingDeltaXEnd; + if (DBG) log("millisleft for animating: " + millisLeft); + if (millisLeft <= 0) { + reset(); + return; + } + // from 0 to 1 as animation progresses + float interpolation = + mInterpolator.getInterpolation((float) millisSoFar / mAnimationDuration); + final int dx = (int) (totalDeltaX * (1 - interpolation)); + mRotaryOffsetX = mAnimatingDeltaXEnd + dx; + if (mDimplesOfFling > 0) { + if (mRotaryOffsetX < 4 * mDimpleSpacing) { + // wrap around on fling left + mRotaryOffsetX += (4 + mDimplesOfFling - 4) * mDimpleSpacing; + } else if (mRotaryOffsetX > 4 * mDimpleSpacing) { + // wrap around on fling right + mRotaryOffsetX -= (4 + mDimplesOfFling - 4) * mDimpleSpacing; + } + } + invalidate(); + } + private void reset() { mAnimating = false; - mTouchDragOffset = 0; + mRotaryOffsetX = 0; + mDimplesOfFling = 0; mGrabbedState = NOTHING_GRABBED; mTriggered = false; } @@ -500,7 +594,8 @@ public class RotarySelector extends View { */ private synchronized void vibrate(long duration) { if (mVibrator == null) { - mVibrator = (android.os.Vibrator) getContext().getSystemService(Context.VIBRATOR_SERVICE); + mVibrator = (android.os.Vibrator) + getContext().getSystemService(Context.VIBRATOR_SERVICE); } mVibrator.vibrate(duration); } diff --git a/libs/rs/rsContext.cpp b/libs/rs/rsContext.cpp index 04f6e07..3e4c9af 100644 --- a/libs/rs/rsContext.cpp +++ b/libs/rs/rsContext.cpp @@ -112,8 +112,8 @@ bool Context::runRootScript() #endif rsAssert(mRootScript->mEnviroment.mIsRoot); - //glColor4f(1,1,1,1); - //glEnable(GL_LIGHT0); + eglQuerySurface(mEGL.mDisplay, mEGL.mSurface, EGL_WIDTH, &mEGL.mWidth); + eglQuerySurface(mEGL.mDisplay, mEGL.mSurface, EGL_HEIGHT, &mEGL.mHeight); glViewport(0, 0, mEGL.mWidth, mEGL.mHeight); glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); glEnable(GL_POINT_SMOOTH); diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaProfileReader.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaProfileReader.java index 0401390..53afb1d 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaProfileReader.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaProfileReader.java @@ -32,6 +32,29 @@ public class MediaProfileReader { return s; } + public static boolean getWMAEnable() { + // push all the property into one big table + int wmaEnable = 1; + wmaEnable = SystemProperties.getInt("ro.media.dec.aud.wma.enabled", + wmaEnable); + if (wmaEnable == 1) { + return true; + } else { + return false; + } + } + + public static boolean getWMVEnable(){ + int wmvEnable = 1; + wmvEnable = SystemProperties.getInt("ro.media.dec.vid.wmv.enabled", + wmvEnable); + if (wmvEnable == 1) { + return true; + } else { + return false; + } + } + public static void createVideoProfileTable() { // push all the property into one big table String encoderType = getVideoCodecProperty(); diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPlayerApiTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPlayerApiTest.java index 30e2d6c..392d1d5 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPlayerApiTest.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPlayerApiTest.java @@ -18,6 +18,7 @@ package com.android.mediaframeworktest.functional; import com.android.mediaframeworktest.MediaFrameworkTest; import com.android.mediaframeworktest.MediaNames; +import com.android.mediaframeworktest.MediaProfileReader; import android.content.Context; import android.test.ActivityInstrumentationTestCase; @@ -35,11 +36,15 @@ import java.io.File; public class MediaPlayerApiTest extends ActivityInstrumentationTestCase<MediaFrameworkTest> { private boolean duratoinWithinTolerence = false; private String TAG = "MediaPlayerApiTest"; + private boolean isWMAEnable = false; + private boolean isWMVEnable = false; Context mContext; public MediaPlayerApiTest() { super("com.android.mediaframeworktest", MediaFrameworkTest.class); + isWMAEnable = MediaProfileReader.getWMAEnable(); + isWMVEnable = MediaProfileReader.getWMVEnable(); } protected void setUp() throws Exception { @@ -82,9 +87,11 @@ public class MediaPlayerApiTest extends ActivityInstrumentationTestCase<MediaFra @MediumTest public void testWMA9GetDuration() throws Exception { - int duration = CodecTest.getDuration(MediaNames.WMA9); - duratoinWithinTolerence = verifyDuration(duration, MediaNames.WMA9_LENGTH); - assertTrue("WMA9 getDuration", duratoinWithinTolerence); + if (isWMAEnable) { + int duration = CodecTest.getDuration(MediaNames.WMA9); + duratoinWithinTolerence = verifyDuration(duration, MediaNames.WMA9_LENGTH); + assertTrue("WMA9 getDuration", duratoinWithinTolerence); + } } @MediumTest @@ -123,8 +130,10 @@ public class MediaPlayerApiTest extends ActivityInstrumentationTestCase<MediaFra @LargeTest public void testWMA9GetCurrentPosition() throws Exception { - boolean currentPosition = CodecTest.getCurrentPosition(MediaNames.WMA9); - assertTrue("WMA9 GetCurrentPosition", currentPosition); + if (isWMAEnable) { + boolean currentPosition = CodecTest.getCurrentPosition(MediaNames.WMA9); + assertTrue("WMA9 GetCurrentPosition", currentPosition); + } } @LargeTest @@ -160,8 +169,10 @@ public class MediaPlayerApiTest extends ActivityInstrumentationTestCase<MediaFra @LargeTest public void testWMA9Pause() throws Exception { - boolean isPaused = CodecTest.pause(MediaNames.WMA9); - assertTrue("WMA9 Pause", isPaused); + if (isWMAEnable) { + boolean isPaused = CodecTest.pause(MediaNames.WMA9); + assertTrue("WMA9 Pause", isPaused); + } } @LargeTest @@ -269,8 +280,10 @@ public class MediaPlayerApiTest extends ActivityInstrumentationTestCase<MediaFra @LargeTest public void testWMA9SeekTo() throws Exception { - boolean isLoop = CodecTest.seekTo(MediaNames.WMA9); - assertTrue("WMA9 seekTo", isLoop); + if (isWMAEnable) { + boolean isLoop = CodecTest.seekTo(MediaNames.WMA9); + assertTrue("WMA9 seekTo", isLoop); + } } @LargeTest @@ -309,8 +322,10 @@ public class MediaPlayerApiTest extends ActivityInstrumentationTestCase<MediaFra @Suppress @LargeTest public void testWMA9SeekToEnd() throws Exception { - boolean isEnd = CodecTest.seekToEnd(MediaNames.WMA9); - assertTrue("WMA9 seekToEnd", isEnd); + if (isWMAEnable) { + boolean isEnd = CodecTest.seekToEnd(MediaNames.WMA9); + assertTrue("WMA9 seekToEnd", isEnd); + } } @LargeTest @@ -327,8 +342,10 @@ public class MediaPlayerApiTest extends ActivityInstrumentationTestCase<MediaFra @LargeTest public void testWAVSeekToEnd() throws Exception { - boolean isEnd = CodecTest.seekToEnd(MediaNames.WAV); - assertTrue("WAV seekToEnd", isEnd); + if (isWMVEnable) { + boolean isEnd = CodecTest.seekToEnd(MediaNames.WAV); + assertTrue("WAV seekToEnd", isEnd); + } } @MediumTest @@ -385,8 +402,12 @@ public class MediaPlayerApiTest extends ActivityInstrumentationTestCase<MediaFra @LargeTest public void testVideoWMVSeekTo() throws Exception { - boolean isSeek = CodecTest.videoSeekTo(MediaNames.VIDEO_WMV); - assertTrue("WMV SeekTo", isSeek); + Log.v(TAG, "wmv not enable"); + if (isWMVEnable) { + Log.v(TAG, "wmv enable"); + boolean isSeek = CodecTest.videoSeekTo(MediaNames.VIDEO_WMV); + assertTrue("WMV SeekTo", isSeek); + } } @LargeTest diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaRecorderTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaRecorderTest.java index b4d265d..690eff6 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaRecorderTest.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaRecorderTest.java @@ -239,7 +239,7 @@ public class MediaRecorderTest extends ActivityInstrumentationTestCase<MediaFram validVideo = true; } Log.v(TAG, "width = " + mOutputVideoWidth + " height = " + mOutputVideoHeight + " Duration = " + mOutputDuration); - removeFile(filePath); + //removeFile(filePath); return validVideo; } @@ -428,8 +428,9 @@ public class MediaRecorderTest extends ActivityInstrumentationTestCase<MediaFram } @LargeTest - //est cases for the new codec + //test cases for the new codec public void testDeviceSpecificCodec() throws Exception { + int noOfFailure = 0; boolean recordSuccess = false; String deviceType = MediaProfileReader.getDeviceType(); Log.v(TAG, "deviceType = " + deviceType); @@ -450,10 +451,18 @@ public class MediaRecorderTest extends ActivityInstrumentationTestCase<MediaFram } else { recordSuccess = recordVideoWithPara(encoder[i], audio[j], "low"); } - assertTrue((encoder[i] + audio[j]), recordSuccess); + if (!recordSuccess){ + Log.v(TAG, "testDeviceSpecificCodec failed"); + Log.v(TAG, "Encoder = " + encoder[i] + "Audio Encoder = " + audio[j]); + noOfFailure++; + } + //assertTrue((encoder[i] + audio[j]), recordSuccess); } } } } + if (noOfFailure != 0){ + assertTrue("testDeviceSpecificCodec", false); + } } } diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java index 1dc51c8..2fa18bf 100644 --- a/services/java/com/android/server/BackupManagerService.java +++ b/services/java/com/android/server/BackupManagerService.java @@ -912,6 +912,7 @@ class BackupManagerService extends IBackupManager.Stub { @Override public void run() { + int status = BackupConstants.TRANSPORT_OK; long startRealtime = SystemClock.elapsedRealtime(); if (DEBUG) Log.v(TAG, "Beginning backup of " + mQueue.size() + " targets"); @@ -920,7 +921,6 @@ class BackupManagerService extends IBackupManager.Stub { try { EventLog.writeEvent(BACKUP_START_EVENT, mTransport.transportDirName()); - int status = BackupConstants.TRANSPORT_OK; // If we haven't stored anything yet, we need to do an init operation. if (status == BackupConstants.TRANSPORT_OK && mEverStoredApps.size() == 0) { @@ -958,11 +958,6 @@ class BackupManagerService extends IBackupManager.Stub { } } - // When we succeed at everything, we can remove the journal - if (status == BackupConstants.TRANSPORT_OK && !mJournal.delete()) { - Log.e(TAG, "Unable to remove backup journal file " + mJournal); - } - if (status == BackupConstants.TRANSPORT_NOT_INITIALIZED) { // The backend reports that our dataset has been wiped. We need to // reset all of our bookkeeping and instead run a new backup pass for @@ -973,7 +968,31 @@ class BackupManagerService extends IBackupManager.Stub { } } catch (Exception e) { Log.e(TAG, "Error in backup thread", e); + status = BackupConstants.TRANSPORT_ERROR; } finally { + // If things went wrong, we need to re-stage the apps we had expected + // to be backing up in this pass. This journals the package names in + // the current active pending-backup file, not in the we are holding + // here in mJournal. + if (status != BackupConstants.TRANSPORT_OK) { + Log.w(TAG, "Backup pass unsuccessful, restaging"); + for (BackupRequest req : mQueue) { + try { + dataChanged(req.appInfo.packageName); + } catch (RemoteException e) { + // can't happen; it's a local call + } + } + } + + // Either backup was successful, in which case we of course do not need + // this pass's journal any more; or it failed, in which case we just + // re-enqueued all of these packages in the current active journal. + // Either way, we no longer need this pass's journal. + if (!mJournal.delete()) { + Log.e(TAG, "Unable to remove backup journal file " + mJournal); + } + // Only once we're entirely finished do we release the wakelock mWakelock.release(); } @@ -1219,7 +1238,7 @@ class BackupManagerService extends IBackupManager.Stub { // build the set of apps to restore try { // TODO: Log this before getAvailableRestoreSets, somehow - EventLog.writeEvent(RESTORE_START_EVENT, mTransport.transportDirName()); + EventLog.writeEvent(RESTORE_START_EVENT, mTransport.transportDirName(), mToken); // Get the list of all packages which have backup enabled. // (Include the Package Manager metadata pseudo-package first.) diff --git a/services/java/com/android/server/DockObserver.java b/services/java/com/android/server/DockObserver.java index 8a4b45d..a70134d 100644 --- a/services/java/com/android/server/DockObserver.java +++ b/services/java/com/android/server/DockObserver.java @@ -74,7 +74,8 @@ class DockObserver extends UEventObserver { if (category != null) { intent = new Intent(Intent.ACTION_MAIN); intent.addCategory(category); - intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK + | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); try { mContext.startActivity(intent); } catch (ActivityNotFoundException e) { diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java index 3c76cf2..38d2304 100644 --- a/services/java/com/android/server/WindowManagerService.java +++ b/services/java/com/android/server/WindowManagerService.java @@ -143,6 +143,7 @@ public class WindowManagerService extends IWindowManager.Stub static final boolean DEBUG_REORDER = false; static final boolean DEBUG_WALLPAPER = false; static final boolean SHOW_TRANSACTIONS = false; + static final boolean HIDE_STACK_CRAWLS = true; static final boolean MEASURE_LATENCY = false; static private LatencyTimer lt; @@ -622,7 +623,7 @@ public class WindowManagerService extends IWindowManager.Stub private void placeWindowAfter(Object pos, WindowState window) { final int i = mWindows.indexOf(pos); - if (localLOGV || DEBUG_FOCUS) Log.v( + if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT) Log.v( TAG, "Adding window " + window + " at " + (i+1) + " of " + mWindows.size() + " (after " + pos + ")"); mWindows.add(i+1, window); @@ -630,7 +631,7 @@ public class WindowManagerService extends IWindowManager.Stub private void placeWindowBefore(Object pos, WindowState window) { final int i = mWindows.indexOf(pos); - if (localLOGV || DEBUG_FOCUS) Log.v( + if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT) Log.v( TAG, "Adding window " + window + " at " + i + " of " + mWindows.size() + " (before " + pos + ")"); mWindows.add(i, window); @@ -687,6 +688,9 @@ public class WindowManagerService extends IWindowManager.Stub //apptoken note that the window could be a floating window //that was created later or a window at the top of the list of //windows associated with this token. + if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT) Log.v( + TAG, "Adding window " + win + " at " + + (newIdx+1) + " of " + N); localmWindows.add(newIdx+1, win); } } @@ -766,9 +770,9 @@ public class WindowManagerService extends IWindowManager.Stub break; } } - if (localLOGV || DEBUG_FOCUS) Log.v( - TAG, "Adding window " + win + " at " - + i + " of " + N); + if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT) Log.v( + TAG, "Adding window " + win + " at " + + i + " of " + N); localmWindows.add(i, win); } } @@ -783,9 +787,9 @@ public class WindowManagerService extends IWindowManager.Stub } } if (i < 0) i = 0; - if (localLOGV || DEBUG_FOCUS) Log.v( - TAG, "Adding window " + win + " at " - + i + " of " + N); + if (DEBUG_FOCUS || DEBUG_WINDOW_MOVEMENT) Log.v( + TAG, "Adding window " + win + " at " + + i + " of " + N); localmWindows.add(i, win); } if (addToToken) { @@ -955,7 +959,7 @@ public class WindowManagerService extends IWindowManager.Stub if (w != null) { if (willMove) { RuntimeException e = new RuntimeException(); - e.fillInStackTrace(); + if (!HIDE_STACK_CRAWLS) e.fillInStackTrace(); if (DEBUG_INPUT_METHOD) Log.w(TAG, "Moving IM target from " + mInputMethodTarget + " to " + w, e); mInputMethodTarget = w; @@ -969,7 +973,7 @@ public class WindowManagerService extends IWindowManager.Stub } if (willMove) { RuntimeException e = new RuntimeException(); - e.fillInStackTrace(); + if (!HIDE_STACK_CRAWLS) e.fillInStackTrace(); if (DEBUG_INPUT_METHOD) Log.w(TAG, "Moving IM target from " + mInputMethodTarget + " to null", e); mInputMethodTarget = null; @@ -982,6 +986,8 @@ public class WindowManagerService extends IWindowManager.Stub int pos = findDesiredInputMethodWindowIndexLocked(true); if (pos >= 0) { win.mTargetAppToken = mInputMethodTarget.mAppToken; + if (DEBUG_WINDOW_MOVEMENT) Log.v( + TAG, "Adding input method window " + win + " at " + pos); mWindows.add(pos, win); moveInputMethodDialogsLocked(pos+1); return; @@ -1022,6 +1028,7 @@ public class WindowManagerService extends IWindowManager.Stub int wpos = mWindows.indexOf(win); if (wpos >= 0) { if (wpos < interestingPos) interestingPos--; + if (DEBUG_WINDOW_MOVEMENT) Log.v(TAG, "Temp removing at " + wpos + ": " + win); mWindows.remove(wpos); int NC = win.mChildWindows.size(); while (NC > 0) { @@ -1030,6 +1037,8 @@ public class WindowManagerService extends IWindowManager.Stub int cpos = mWindows.indexOf(cw); if (cpos >= 0) { if (cpos < interestingPos) interestingPos--; + if (DEBUG_WINDOW_MOVEMENT) Log.v(TAG, "Temp removing child at " + + cpos + ": " + cw); mWindows.remove(cpos); } } @@ -1044,6 +1053,8 @@ public class WindowManagerService extends IWindowManager.Stub // this case should be rare, so it shouldn't be that big a deal. int wpos = mWindows.indexOf(win); if (wpos >= 0) { + if (DEBUG_WINDOW_MOVEMENT) Log.v(TAG, "ReAdd removing from " + wpos + + ": " + win); mWindows.remove(wpos); reAddWindowLocked(wpos, win); } @@ -1472,6 +1483,8 @@ public class WindowManagerService extends IWindowManager.Stub // not in the list. int oldIndex = localmWindows.indexOf(wallpaper); if (oldIndex >= 0) { + if (DEBUG_WINDOW_MOVEMENT) Log.v(TAG, "Wallpaper removing at " + + oldIndex + ": " + wallpaper); localmWindows.remove(oldIndex); if (oldIndex < foundI) { foundI--; @@ -1479,7 +1492,8 @@ public class WindowManagerService extends IWindowManager.Stub } // Now stick it in. - if (DEBUG_WALLPAPER) Log.v(TAG, "Moving wallpaper " + wallpaper + if (DEBUG_WALLPAPER || DEBUG_WINDOW_MOVEMENT) Log.v(TAG, + "Moving wallpaper " + wallpaper + " from " + oldIndex + " to " + foundI); localmWindows.add(foundI, wallpaper); @@ -2003,6 +2017,7 @@ public class WindowManagerService extends IWindowManager.Stub mWindowMap.remove(win.mClient.asBinder()); mWindows.remove(win); + if (DEBUG_WINDOW_MOVEMENT) Log.v(TAG, "Final remove of window: " + win); if (mInputMethodWindow == win) { mInputMethodWindow = null; @@ -2447,7 +2462,7 @@ public class WindowManagerService extends IWindowManager.Stub if (a != null) { if (DEBUG_ANIM) { RuntimeException e = new RuntimeException(); - e.fillInStackTrace(); + if (!HIDE_STACK_CRAWLS) e.fillInStackTrace(); Log.v(TAG, "Loaded animation " + a + " for " + win, e); } win.setAnimation(a); @@ -2551,7 +2566,7 @@ public class WindowManagerService extends IWindowManager.Stub if (a != null) { if (DEBUG_ANIM) { RuntimeException e = new RuntimeException(); - e.fillInStackTrace(); + if (!HIDE_STACK_CRAWLS) e.fillInStackTrace(); Log.v(TAG, "Loaded animation " + a + " for " + wtoken, e); } wtoken.setAnimation(a); @@ -3093,6 +3108,8 @@ public class WindowManagerService extends IWindowManager.Stub startingWindow.mToken = wtoken; startingWindow.mRootToken = wtoken; startingWindow.mAppToken = wtoken; + if (DEBUG_WINDOW_MOVEMENT) Log.v(TAG, + "Removing starting window: " + startingWindow); mWindows.remove(startingWindow); ttoken.windows.remove(startingWindow); ttoken.allAppWindows.remove(startingWindow); @@ -3320,7 +3337,7 @@ public class WindowManagerService extends IWindowManager.Stub if (DEBUG_APP_TRANSITIONS || DEBUG_ORIENTATION) { RuntimeException e = new RuntimeException(); - e.fillInStackTrace(); + if (!HIDE_STACK_CRAWLS) e.fillInStackTrace(); Log.v(TAG, "setAppVisibility(" + token + ", " + visible + "): mNextAppTransition=" + mNextAppTransition + " hidden=" + wtoken.hidden @@ -3412,7 +3429,7 @@ public class WindowManagerService extends IWindowManager.Stub int configChanges) { if (DEBUG_ORIENTATION) { RuntimeException e = new RuntimeException(); - e.fillInStackTrace(); + if (!HIDE_STACK_CRAWLS) e.fillInStackTrace(); Log.i(TAG, "Set freezing of " + wtoken.appToken + ": hidden=" + wtoken.hidden + " freezing=" + wtoken.freezingScreen, e); @@ -3512,6 +3529,12 @@ public class WindowManagerService extends IWindowManager.Stub if (delayed) { // set the token aside because it has an active animation to be finished mExitingAppTokens.add(wtoken); + } else { + // Make sure there is no animation running on this token, + // so any windows associated with it will be removed as + // soon as their animations are complete + wtoken.animation = null; + wtoken.animating = false; } mAppTokens.remove(wtoken); wtoken.removed = true; @@ -3547,7 +3570,7 @@ public class WindowManagerService extends IWindowManager.Stub final int NW = token.windows.size(); for (int i=0; i<NW; i++) { WindowState win = token.windows.get(i); - if (DEBUG_WINDOW_MOVEMENT) Log.v(TAG, "Tmp removing window " + win); + if (DEBUG_WINDOW_MOVEMENT) Log.v(TAG, "Tmp removing app window " + win); mWindows.remove(win); int j = win.mChildWindows.size(); while (j > 0) { @@ -6999,13 +7022,13 @@ public class WindowManagerService extends IWindowManager.Stub try { if (DEBUG_VISIBILITY) { RuntimeException e = new RuntimeException(); - e.fillInStackTrace(); + if (!HIDE_STACK_CRAWLS) e.fillInStackTrace(); Log.w(TAG, "Window " + this + " destroying surface " + mSurface + ", session " + mSession, e); } if (SHOW_TRANSACTIONS) { RuntimeException ex = new RuntimeException(); - ex.fillInStackTrace(); + if (!HIDE_STACK_CRAWLS) ex.fillInStackTrace(); Log.i(TAG, " SURFACE " + mSurface + ": DESTROY (" + mAttrs.getTitle() + ")", ex); } @@ -7060,7 +7083,7 @@ public class WindowManagerService extends IWindowManager.Stub boolean performShowLocked() { if (DEBUG_VISIBILITY) { RuntimeException e = new RuntimeException(); - e.fillInStackTrace(); + if (!HIDE_STACK_CRAWLS) e.fillInStackTrace(); Log.v(TAG, "performShow on " + this + ": readyToShow=" + mReadyToShow + " readyForDisplay=" + isReadyForDisplay() + " starting=" + (mAttrs.type == TYPE_APPLICATION_STARTING), e); @@ -8662,6 +8685,7 @@ public class WindowManagerService extends IWindowManager.Stub final void rebuildAppWindowListLocked() { int NW = mWindows.size(); int i; + int numRemoved = 0; // First remove all existing app windows. i=0; @@ -8671,6 +8695,7 @@ public class WindowManagerService extends IWindowManager.Stub if (DEBUG_WINDOW_MOVEMENT) Log.v(TAG, "Rebuild removing window: " + win); NW--; + numRemoved++; continue; } i++; @@ -8691,6 +8716,11 @@ public class WindowManagerService extends IWindowManager.Stub for (int j=0; j<NT; j++) { i = reAddAppWindowsLocked(i, mAppTokens.get(j)); } + + if (i != numRemoved) { + Log.w(TAG, "Rebuild removed " + numRemoved + + " windows but added " + i); + } } private final void assignLayersLocked() { @@ -8853,13 +8883,34 @@ public class WindowManagerService extends IWindowManager.Stub } } - if (!mPolicy.finishLayoutLw()) { + int changes = mPolicy.finishLayoutLw(); + if ((changes&WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER) != 0) { + if ((adjustWallpaperWindowsLocked()&ADJUST_WALLPAPER_LAYERS_CHANGED) != 0) { + assignLayersLocked(); + } + } + if (changes == 0) { mLayoutNeeded = false; } else if (repeats > 2) { Log.w(TAG, "Layout repeat aborted after too many iterations"); mLayoutNeeded = false; + if ((changes&WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG) != 0) { + Configuration newConfig = updateOrientationFromAppTokensLocked( + null, null); + if (newConfig != null) { + mLayoutNeeded = true; + mH.sendEmptyMessage(H.COMPUTE_AND_SEND_NEW_CONFIGURATION); + } + } } else { repeats++; + if ((changes&WindowManagerPolicy.FINISH_LAYOUT_REDO_CONFIG) != 0) { + Configuration newConfig = updateOrientationFromAppTokensLocked( + null, null); + if (newConfig != null) { + mH.sendEmptyMessage(H.COMPUTE_AND_SEND_NEW_CONFIGURATION); + } + } } } } @@ -9784,6 +9835,11 @@ public class WindowManagerService extends IWindowManager.Stub for (i=mExitingAppTokens.size()-1; i>=0; i--) { AppWindowToken token = mExitingAppTokens.get(i); if (!token.hasVisible && !mClosingApps.contains(token)) { + // Make sure there is no animation running on this token, + // so any windows associated with it will be removed as + // soon as their animations are complete + token.animation = null; + token.animating = false; mAppTokens.remove(token); mExitingAppTokens.remove(i); } |