OSDN Git Service

Add system wide management of core settings
authorSvetoslav Ganov <svetoslavganov@google.com>
Wed, 2 Mar 2011 20:58:40 +0000 (12:58 -0800)
committerSvetoslav Ganov <svetoslavganov@google.com>
Thu, 3 Mar 2011 02:22:49 +0000 (18:22 -0800)
bug:3505060

Since we want to have some settings that are used very frequently
by many applications (long-press timeout is one example) these should
be managed efficiently to reduce lookups from different processes
because in the case of a cache miss a disk I/O is performed. Now
the system manages such core settings and propagates them to the
application processes.

Change-Id: Ie793211baf8770f2181ac8ba9d7c2609dfaa32a7

core/java/android/app/ActivityThread.java
core/java/android/app/AppGlobals.java
core/java/android/app/ApplicationThreadNative.java
core/java/android/app/IApplicationThread.java
core/java/android/provider/Settings.java
core/java/android/view/ViewConfiguration.java
packages/SettingsProvider/res/values/defaults.xml
packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java
services/java/com/android/server/am/ActivityManagerService.java
services/java/com/android/server/am/CoreSettingsObserver.java [new file with mode: 0644]

index 8f9a76b..b409f2f 100644 (file)
@@ -98,7 +98,6 @@ import java.util.TimeZone;
 import java.util.regex.Pattern;
 
 import dalvik.system.CloseGuard;
-import dalvik.system.SamplingProfiler;
 
 final class SuperNotCalledException extends AndroidRuntimeException {
     public SuperNotCalledException(String msg) {
@@ -355,6 +354,7 @@ public final class ActivityThread {
         boolean restrictedBackupMode;
         Configuration config;
         boolean handlingProfiling;
+        Bundle coreSettings;
         public String toString() {
             return "AppBindData{appInfo=" + appInfo + "}";
         }
@@ -552,7 +552,7 @@ public final class ActivityThread {
                 ComponentName instrumentationName, String profileFile,
                 Bundle instrumentationArgs, IInstrumentationWatcher instrumentationWatcher,
                 int debugMode, boolean isRestrictedBackupMode, Configuration config,
-                Map<String, IBinder> services) {
+                Map<String, IBinder> services, Bundle coreSettings) {
 
             if (services != null) {
                 // Setup the service cache in the ServiceManager
@@ -570,6 +570,7 @@ public final class ActivityThread {
             data.debugMode = debugMode;
             data.restrictedBackupMode = isRestrictedBackupMode;
             data.config = config;
+            data.coreSettings = coreSettings;
             queueOrSendMessage(H.BIND_APPLICATION, data);
         }
 
@@ -896,6 +897,10 @@ public final class ActivityThread {
         private void printRow(PrintWriter pw, String format, Object...objs) {
             pw.println(String.format(format, objs));
         }
+
+        public void setCoreSettings(Bundle settings) {
+            queueOrSendMessage(H.SET_CORE_SETTINGS, settings);
+        }
     }
 
     private final class H extends Handler {
@@ -937,6 +942,7 @@ public final class ActivityThread {
         public static final int DUMP_HEAP               = 135;
         public static final int DUMP_ACTIVITY           = 136;
         public static final int SLEEPING                = 137;
+        public static final int SET_CORE_SETTINGS       = 138;
         String codeToString(int code) {
             if (DEBUG_MESSAGES) {
                 switch (code) {
@@ -978,6 +984,7 @@ public final class ActivityThread {
                     case DUMP_HEAP: return "DUMP_HEAP";
                     case DUMP_ACTIVITY: return "DUMP_ACTIVITY";
                     case SLEEPING: return "SLEEPING";
+                    case SET_CORE_SETTINGS: return "SET_CORE_SETTINGS";
                 }
             }
             return "(unknown)";
@@ -1113,6 +1120,9 @@ public final class ActivityThread {
                 case SLEEPING:
                     handleSleeping((IBinder)msg.obj, msg.arg1 != 0);
                     break;
+                case SET_CORE_SETTINGS:
+                    handleSetCoreSettings((Bundle) msg.obj);
+                    break;
             }
             if (DEBUG_MESSAGES) Slog.v(TAG, "<<< done: " + msg.what);
         }
@@ -2709,6 +2719,14 @@ public final class ActivityThread {
         }
     }
 
+    private void handleSetCoreSettings(Bundle coreSettings) {
+        if (mBoundApplication != null) {
+            synchronized (mBoundApplication) {
+                mBoundApplication.coreSettings = coreSettings;
+            }
+        }
+    }
+
     private final void deliverResults(ActivityClientRecord r, List<ResultInfo> results) {
         final int N = results.size();
         for (int i=0; i<N; i++) {
@@ -3971,6 +3989,20 @@ public final class ActivityThread {
         }
     }
 
+    public int getIntCoreSetting(String key, int defaultValue) {
+        if (mBoundApplication == null) {
+            return defaultValue;
+        }
+        synchronized (mBoundApplication) {
+            Bundle coreSettings = mBoundApplication.coreSettings;
+            if (coreSettings != null) {
+                return coreSettings.getInt(key, defaultValue);
+            } else {
+                return defaultValue;
+            }
+        }
+    }
+
     public static final void main(String[] args) {
         SamplingProfilerIntegration.start();
 
index 9a39129..55515b8 100644 (file)
@@ -38,12 +38,23 @@ public class AppGlobals {
     public static String getInitialPackage() {
         return ActivityThread.currentPackageName();
     }
-    
+
     /**
      * Return the raw interface to the package manager.
-     * @return
+     * @return The package manager.
      */
     public static IPackageManager getPackageManager() {
         return ActivityThread.getPackageManager();
     }
+
+    /**
+     * Gets the value of an integer core setting.
+     *
+     * @param key The setting key.
+     * @param defaultValue The setting default value.
+     * @return The core settings.
+     */
+    public static int getIntCoreSetting(String key, int defaultValue) {
+        return ActivityThread.currentActivityThread().getIntCoreSetting(key, defaultValue);
+    }
 }
index ef92933..aa26b04 100644 (file)
@@ -257,10 +257,11 @@ public abstract class ApplicationThreadNative extends Binder
             boolean restrictedBackupMode = (data.readInt() != 0);
             Configuration config = Configuration.CREATOR.createFromParcel(data);
             HashMap<String, IBinder> services = data.readHashMap(null);
+            Bundle coreSettings = data.readBundle();
             bindApplication(packageName, info,
                             providers, testName, profileName,
                             testArgs, testWatcher, testMode, restrictedBackupMode,
-                            config, services);
+                            config, services, coreSettings);
             return true;
         }
         
@@ -454,6 +455,13 @@ public abstract class ApplicationThreadNative extends Binder
             }
             return true;
         }
+
+        case SET_CORE_SETTINGS: {
+            data.enforceInterface(IApplicationThread.descriptor);
+            Bundle settings = data.readBundle();
+            setCoreSettings(settings);
+            return true;
+        }
         }
 
         return super.onTransact(code, data, reply, flags);
@@ -712,7 +720,7 @@ class ApplicationThreadProxy implements IApplicationThread {
             List<ProviderInfo> providers, ComponentName testName,
             String profileName, Bundle testArgs, IInstrumentationWatcher testWatcher, int debugMode,
             boolean restrictedBackupMode, Configuration config,
-            Map<String, IBinder> services) throws RemoteException {
+            Map<String, IBinder> services, Bundle coreSettings) throws RemoteException {
         Parcel data = Parcel.obtain();
         data.writeInterfaceToken(IApplicationThread.descriptor);
         data.writeString(packageName);
@@ -731,6 +739,7 @@ class ApplicationThreadProxy implements IApplicationThread {
         data.writeInt(restrictedBackupMode ? 1 : 0);
         config.writeToParcel(data, 0);
         data.writeMap(services);
+        data.writeBundle(coreSettings);
         mRemote.transact(BIND_APPLICATION_TRANSACTION, data, null,
                 IBinder.FLAG_ONEWAY);
         data.recycle();
@@ -938,4 +947,11 @@ class ApplicationThreadProxy implements IApplicationThread {
         mRemote.transact(DUMP_ACTIVITY_TRANSACTION, data, null, 0);
         data.recycle();
     }
+
+    public void setCoreSettings(Bundle coreSettings) throws RemoteException {
+        Parcel data = Parcel.obtain();
+        data.writeInterfaceToken(IApplicationThread.descriptor);
+        data.writeBundle(coreSettings);
+        mRemote.transact(SET_CORE_SETTINGS, data, null, IBinder.FLAG_ONEWAY);
+    }
 }
index 16c3c5c..55177a9 100644 (file)
@@ -82,7 +82,8 @@ public interface IApplicationThread extends IInterface {
     void bindApplication(String packageName, ApplicationInfo info, List<ProviderInfo> providers,
             ComponentName testName, String profileName, Bundle testArguments, 
             IInstrumentationWatcher testWatcher, int debugMode, boolean restrictedBackupMode,
-            Configuration config, Map<String, IBinder> services) throws RemoteException;
+            Configuration config, Map<String, IBinder> services,
+            Bundle coreSettings) throws RemoteException;
     void scheduleExit() throws RemoteException;
     void scheduleSuicide() throws RemoteException;
     void requestThumbnail(IBinder token) throws RemoteException;
@@ -110,6 +111,7 @@ public interface IApplicationThread extends IInterface {
     void scheduleCrash(String msg) throws RemoteException;
     void dumpActivity(FileDescriptor fd, IBinder servicetoken, String prefix, String[] args)
             throws RemoteException;
+    void setCoreSettings(Bundle coreSettings) throws RemoteException;
 
     String descriptor = "android.app.IApplicationThread";
 
@@ -151,4 +153,5 @@ public interface IApplicationThread extends IInterface {
     int DUMP_ACTIVITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+36;
     int CLEAR_DNS_CACHE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+37;
     int SET_HTTP_PROXY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+38;
+    int SET_CORE_SETTINGS = IBinder.FIRST_CALL_TRANSACTION+39;
 }
index 1718189..6deb5a0 100644 (file)
@@ -2698,6 +2698,12 @@ public final class Settings {
             "accessibility_web_content_key_bindings";
 
         /**
+         * The timout for considering a press to be a long press in milliseconds.
+         * @hide
+         */
+        public static final String LONG_PRESS_TIMEOUT = "long_press_timeout";
+
+        /**
          * Setting to always use the default text-to-speech settings regardless
          * of the application settings.
          * 1 = override application settings,
index cc4e89c..d95c5b0 100644 (file)
 
 package android.view;
 
+import android.app.AppGlobals;
 import android.content.Context;
 import android.content.res.Configuration;
+import android.os.Bundle;
+import android.provider.Settings;
 import android.util.DisplayMetrics;
 import android.util.SparseArray;
 
@@ -74,7 +77,7 @@ public class ViewConfiguration {
      * Defines the duration in milliseconds before a press turns into
      * a long press
      */
-    private static final int LONG_PRESS_TIMEOUT = 500;
+    private static final int DEFAULTLONG_PRESS_TIMEOUT = 500;
     
     /**
      * Defines the duration in milliseconds a user needs to hold down the
@@ -320,15 +323,16 @@ public class ViewConfiguration {
     public static int getPressedStateDuration() {
         return PRESSED_STATE_DURATION;
     }
-    
+
     /**
      * @return the duration in milliseconds before a press turns into
      * a long press
      */
     public static int getLongPressTimeout() {
-        return LONG_PRESS_TIMEOUT;
+        return AppGlobals.getIntCoreSetting(Settings.Secure.LONG_PRESS_TIMEOUT,
+                DEFAULTLONG_PRESS_TIMEOUT);
     }
-    
+
     /**
      * @return the duration in milliseconds we will wait to see if a touch event
      * is a tap or a scroll. If the user does not move within this interval, it is
index 2df6d68..bf06f94 100644 (file)
     <integer name="def_download_manager_max_bytes_over_mobile">-1</integer>
     <!-- Default for Settings.Secure.DOWNLOAD_RECOMMENDED_MAX_BYTES_OVER_MOBILE. <=0 if no limit -->
     <integer name="def_download_manager_recommended_max_bytes_over_mobile">-1</integer>
+
+    <!-- Default for Settings.Secure.LONG_PRESS_TIMEOUT_MILLIS -->
+    <integer name="def_long_press_timeout_millis">500</integer>
+
 </resources>
index f336f06..d901c2c 100644 (file)
@@ -819,6 +819,24 @@ public class DatabaseHelper extends SQLiteOpenHelper {
              upgradeVersion = 64;
          }
 
+        if (upgradeVersion == 64) {
+            // New setting to configure the long press timeout.
+            db.beginTransaction();
+            SQLiteStatement stmt = null;
+            try {
+                stmt = db.compileStatement("INSERT INTO secure(name,value)"
+                        + " VALUES(?,?);");
+                loadIntegerSetting(stmt, Settings.Secure.LONG_PRESS_TIMEOUT,
+                        R.integer.def_long_press_timeout_millis);
+                stmt.close();
+                db.setTransactionSuccessful();
+            } finally {
+                db.endTransaction();
+                if (stmt != null) stmt.close();
+            }
+            upgradeVersion = 65;
+        }
+
         // *** Remember to update DATABASE_VERSION above!
 
         if (upgradeVersion != currentVersion) {
@@ -1332,6 +1350,9 @@ public class DatabaseHelper extends SQLiteOpenHelper {
                 loadSetting(stmt, Settings.Secure.DOWNLOAD_RECOMMENDED_MAX_BYTES_OVER_MOBILE,
                         Integer.toString(recommendedMaxBytes));
             }
+
+            loadIntegerSetting(stmt, Settings.Secure.LONG_PRESS_TIMEOUT,
+                    R.integer.def_long_press_timeout_millis);
         } finally {
             if (stmt != null) stmt.close();
         }
index e6dfb7f..40417b1 100644 (file)
@@ -717,6 +717,8 @@ public final class ActivityManagerService extends ActivityManagerNative
     final private SparseArray<HashMap<Uri, UriPermission>> mGrantedUriPermissions
             = new SparseArray<HashMap<Uri, UriPermission>>();
 
+    CoreSettingsObserver mCoreSettingsObserver;
+
     /**
      * Thread-local storage used to carry caller permissions over through
      * indirect content-provider access.
@@ -3589,7 +3591,8 @@ public final class ActivityManagerService extends ActivityManagerNative
                     app.instrumentationClass, app.instrumentationProfileFile,
                     app.instrumentationArguments, app.instrumentationWatcher, testMode, 
                     isRestrictedBackupMode || !normalMode,
-                    mConfiguration, getCommonServicesLocked());
+                    mConfiguration, getCommonServicesLocked(),
+                    mCoreSettingsObserver.getCoreSettingsLocked());
             updateLruProcessLocked(app, false, true);
             app.lastRequestedGc = app.lastLowMemory = SystemClock.uptimeMillis();
         } catch (Exception e) {
@@ -5772,6 +5775,8 @@ public final class ActivityManagerService extends ActivityManagerNative
         if (providers != null) {
             mSystemThread.installSystemProviders(providers);
         }
+
+        mSelf.mCoreSettingsObserver = new CoreSettingsObserver(mSelf);
     }
 
     /**
@@ -13030,4 +13035,17 @@ public final class ActivityManagerService extends ActivityManagerNative
     public void monitor() {
         synchronized (this) { }
     }
+
+    public void onCoreSettingsChange(Bundle settings) {
+        for (int i = mLruProcesses.size() - 1; i >= 0; i--) {
+            ProcessRecord processRecord = mLruProcesses.get(i);
+            try {
+                if (processRecord.thread != null) {
+                    processRecord.thread.setCoreSettings(settings);
+                }
+            } catch (RemoteException re) {
+                /* ignore */
+            }
+        }
+    }
 }
diff --git a/services/java/com/android/server/am/CoreSettingsObserver.java b/services/java/com/android/server/am/CoreSettingsObserver.java
new file mode 100644 (file)
index 0000000..25db84a
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2006-2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.am;
+
+import android.content.Context;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.os.Bundle;
+import android.provider.Settings;
+import android.provider.Settings.SettingNotFoundException;
+import android.util.Log;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Helper class for watching a set of core settings which the framework
+ * propagates to application processes to avoid multiple lookups and potentially
+ * disk I/O operations. Note: This class assumes that all core settings reside
+ * in {@link Settings.Secure}.
+ */
+class CoreSettingsObserver extends ContentObserver {
+    private static final String LOG_TAG = CoreSettingsObserver.class.getSimpleName();
+
+    // mapping form property name to its type
+    private static final Map<String, Class<?>> sCoreSettingToTypeMap = new HashMap<
+            String, Class<?>>();
+    static {
+        sCoreSettingToTypeMap.put(Settings.Secure.LONG_PRESS_TIMEOUT, int.class);
+        // add other core settings here...
+    }
+
+    private final Bundle mCoreSettings = new Bundle();
+
+    private final ActivityManagerService mActivityManagerService;
+
+    public CoreSettingsObserver(ActivityManagerService activityManagerService) {
+        super(activityManagerService.mHandler);
+        mActivityManagerService = activityManagerService;
+        beginObserveCoreSettings();
+        populateCoreSettings(mCoreSettings);
+    }
+
+    public Bundle getCoreSettingsLocked() {
+        return (Bundle) mCoreSettings.clone();
+    }
+
+    @Override
+    public void onChange(boolean selfChange) {
+        synchronized (mActivityManagerService) {
+            populateCoreSettings(mCoreSettings);
+            mActivityManagerService.onCoreSettingsChange(mCoreSettings);
+        }
+    }
+
+    private void beginObserveCoreSettings() {
+        for (String setting : sCoreSettingToTypeMap.keySet()) {
+            Uri uri = Settings.Secure.getUriFor(setting);
+            mActivityManagerService.mContext.getContentResolver().registerContentObserver(
+                    uri, false, this);
+        }
+    }
+
+    private void populateCoreSettings(Bundle snapshot) {
+        Context context = mActivityManagerService.mContext;
+        for (Map.Entry<String, Class<?>> entry : sCoreSettingToTypeMap.entrySet()) {
+            String setting = entry.getKey();
+            Class<?> type = entry.getValue();
+            try {
+                if (type == String.class) {
+                    String value = Settings.Secure.getString(context.getContentResolver(),
+                            setting);
+                    snapshot.putString(setting, value);
+                } else if (type == int.class) {
+                    int value = Settings.Secure.getInt(context.getContentResolver(),
+                            setting);
+                    snapshot.putInt(setting, value);
+                } else if (type == float.class) {
+                    float value = Settings.Secure.getFloat(context.getContentResolver(),
+                            setting);
+                    snapshot.putFloat(setting, value);
+                } else if (type == long.class) {
+                    long value = Settings.Secure.getLong(context.getContentResolver(),
+                            setting);
+                    snapshot.putLong(setting, value);
+                }
+            } catch (SettingNotFoundException snfe) {
+                Log.w(LOG_TAG, "Cannot find setting \"" + setting + "\"", snfe);
+            }
+        }
+    }
+}