diff options
9 files changed, 228 insertions, 252 deletions
diff --git a/core/java/android/app/SearchDialog.java b/core/java/android/app/SearchDialog.java index 44d1eaa..0785029 100644 --- a/core/java/android/app/SearchDialog.java +++ b/core/java/android/app/SearchDialog.java @@ -242,8 +242,15 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS // Reset any stored values from last time dialog was shown. mStoredComponentName = null; mStoredAppSearchData = null; - - return doShow(initialQuery, selectInitialQuery, componentName, appSearchData, globalSearch); + + boolean success = doShow(initialQuery, selectInitialQuery, componentName, appSearchData, + globalSearch); + if (success) { + // Display the drop down as soon as possible instead of waiting for the rest of the + // pending UI stuff to get done, so that things appear faster to the user. + mSearchAutoComplete.showDropDownAfterLayout(); + } + return success; } /** diff --git a/core/java/android/server/search/SearchableInfo.java b/core/java/android/server/search/SearchableInfo.java index 90dfa0b..8ef1f15 100644 --- a/core/java/android/server/search/SearchableInfo.java +++ b/core/java/android/server/search/SearchableInfo.java @@ -67,6 +67,7 @@ public final class SearchableInfo implements Parcelable { private final int mSearchImeOptions; private final boolean mIncludeInGlobalSearch; private final boolean mQueryAfterZeroResults; + private final String mSettingsDescription; private final String mSuggestAuthority; private final String mSuggestPath; private final String mSuggestSelection; @@ -134,6 +135,14 @@ public final class SearchableInfo implements Parcelable { public boolean shouldRewriteQueryFromText() { return 0 != (mSearchMode & SEARCH_MODE_QUERY_REWRITE_FROM_TEXT); } + + /** + * Gets the description to use for this source in system search settings, or null if + * none has been specified. + */ + public String getSettingsDescription() { + return mSettingsDescription; + } /** * Retrieve the path for obtaining search suggestions. @@ -280,6 +289,8 @@ public final class SearchableInfo implements Parcelable { mQueryAfterZeroResults = a.getBoolean( com.android.internal.R.styleable.Searchable_queryAfterZeroResults, false); + mSettingsDescription = a.getString( + com.android.internal.R.styleable.Searchable_searchSettingsDescription); mSuggestAuthority = a.getString( com.android.internal.R.styleable.Searchable_searchSuggestAuthority); mSuggestPath = a.getString( @@ -448,6 +459,7 @@ public final class SearchableInfo implements Parcelable { + ",suggestAuthority=" + searchable.getSuggestAuthority() + ",target=" + searchable.getSearchActivity().getClassName() + ",global=" + searchable.shouldIncludeInGlobalSearch() + + ",settingsDescription=" + searchable.getSettingsDescription() + ",threshold=" + searchable.getSuggestThreshold()); } else { Log.d(LOG_TAG, "Checked " + activityInfo.name + ", no searchable meta-data"); @@ -686,7 +698,8 @@ public final class SearchableInfo implements Parcelable { mSearchImeOptions = in.readInt(); mIncludeInGlobalSearch = in.readInt() != 0; mQueryAfterZeroResults = in.readInt() != 0; - + + mSettingsDescription = in.readString(); mSuggestAuthority = in.readString(); mSuggestPath = in.readString(); mSuggestSelection = in.readString(); @@ -723,6 +736,7 @@ public final class SearchableInfo implements Parcelable { dest.writeInt(mIncludeInGlobalSearch ? 1 : 0); dest.writeInt(mQueryAfterZeroResults ? 1 : 0); + dest.writeString(mSettingsDescription); dest.writeString(mSuggestAuthority); dest.writeString(mSuggestPath); dest.writeString(mSuggestSelection); diff --git a/core/java/android/widget/AutoCompleteTextView.java b/core/java/android/widget/AutoCompleteTextView.java index e84e5b0..2182384 100644 --- a/core/java/android/widget/AutoCompleteTextView.java +++ b/core/java/android/widget/AutoCompleteTextView.java @@ -124,6 +124,7 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe private boolean mBlockCompletion; private AutoCompleteTextView.ListSelectorHider mHideSelector; + private Runnable mShowDropDownRunnable; private AutoCompleteTextView.PassThroughClickListener mPassThroughClickListener; @@ -1080,6 +1081,15 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe } /** + * Issues a runnable to show the dropdown as soon as possible. + * + * @hide internal used only by Search Dialog + */ + public void showDropDownAfterLayout() { + post(mShowDropDownRunnable); + } + + /** * <p>Displays the drop down on screen.</p> */ public void showDropDown() { @@ -1190,6 +1200,22 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe mHideSelector = new ListSelectorHider(); + /** + * This Runnable exists for the sole purpose of checking if the view layout has got + * completed and if so call showDropDown to display the drop down. This is used to show + * the drop down as soon as possible after user opens up the search dialog, without + * waiting for the normal UI pipeline to do it's job which is slower than this method. + */ + mShowDropDownRunnable = new Runnable() { + public void run() { + // View layout should be all done before displaying the drop down. + View view = getDropDownAnchorView(); + if (view != null && view.getWindowToken() != null) { + showDropDown(); + } + } + }; + mDropDownList = new DropDownListView(context); mDropDownList.setSelector(mDropDownListHighlight); mDropDownList.setAdapter(mAdapter); diff --git a/packages/VpnServices/src/com/android/server/vpn/AndroidServiceProxy.java b/packages/VpnServices/src/com/android/server/vpn/AndroidServiceProxy.java index 2ad218f..7dd9d9e 100644 --- a/packages/VpnServices/src/com/android/server/vpn/AndroidServiceProxy.java +++ b/packages/VpnServices/src/com/android/server/vpn/AndroidServiceProxy.java @@ -78,20 +78,10 @@ public class AndroidServiceProxy extends ProcessProxy { /** * Sends a command with arguments to the service through the control socket. - * Each argument is sent as a C-style zero-terminated string. */ public void sendCommand(String ...args) throws IOException { OutputStream out = getControlSocketOutput(); for (String arg : args) outputString(out, arg); - checkSocketResult(); - } - - /** - * Sends a command with arguments to the service through the control socket. - */ - public void sendCommand2(String ...args) throws IOException { - OutputStream out = getControlSocketOutput(); - for (String arg : args) outputString2(out, arg); out.write(END_OF_ARGUMENTS); out.flush(); checkSocketResult(); @@ -128,8 +118,9 @@ public class AndroidServiceProxy extends ProcessProxy { if (data == 0) { // re-establish the connection: - // synchronized here so that checkSocketResult() returns - // when new mKeepaliveSocket is available for next cmd + // synchronized here so that checkSocketResult() + // returns when new mKeepaliveSocket is available for + // next cmd synchronized (this) { setResultAndCloseControlSocket((byte) data); s = mKeepaliveSocket = createServiceSocket(); @@ -244,12 +235,6 @@ public class AndroidServiceProxy extends ProcessProxy { } private void outputString(OutputStream out, String s) throws IOException { - out.write(s.getBytes()); - out.write(0); - out.flush(); - } - - private void outputString2(OutputStream out, String s) throws IOException { byte[] bytes = s.getBytes(); out.write(bytes.length); out.write(bytes); diff --git a/packages/VpnServices/src/com/android/server/vpn/L2tpIpsecPskService.java b/packages/VpnServices/src/com/android/server/vpn/L2tpIpsecPskService.java new file mode 100644 index 0000000..6abf81c --- /dev/null +++ b/packages/VpnServices/src/com/android/server/vpn/L2tpIpsecPskService.java @@ -0,0 +1,49 @@ +/* + * 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 com.android.server.vpn; + +import android.net.vpn.L2tpIpsecPskProfile; + +import java.io.IOException; + +/** + * The service that manages the preshared key based L2TP-over-IPSec VPN + * connection. + */ +class L2tpIpsecPskService extends VpnService<L2tpIpsecPskProfile> { + private static final String IPSEC_DAEMON = "racoon"; + + @Override + protected void connect(String serverIp, String username, String password) + throws IOException { + String hostIp = getHostIp(); + L2tpIpsecPskProfile p = getProfile(); + + // IPSEC + AndroidServiceProxy ipsecService = startService(IPSEC_DAEMON); + ipsecService.sendCommand(hostIp, serverIp, L2tpService.L2TP_PORT, + p.getPresharedKey()); + + sleep(2000); // 2 seconds + + // L2TP + MtpdHelper.sendCommand(this, L2tpService.L2TP_DAEMON, serverIp, + L2tpService.L2TP_PORT, + (p.isSecretEnabled() ? p.getSecretString() : null), + username, password); + } +} diff --git a/packages/VpnServices/src/com/android/server/vpn/L2tpIpsecService.java b/packages/VpnServices/src/com/android/server/vpn/L2tpIpsecService.java index 877fa6b..bd14110 100644 --- a/packages/VpnServices/src/com/android/server/vpn/L2tpIpsecService.java +++ b/packages/VpnServices/src/com/android/server/vpn/L2tpIpsecService.java @@ -22,7 +22,7 @@ import android.security.Keystore; import java.io.IOException; /** - * The service that manages the L2TP-over-IPSec VPN connection. + * The service that manages the certificate based L2TP-over-IPSec VPN connection. */ class L2tpIpsecService extends VpnService<L2tpIpsecProfile> { private static final String IPSEC_DAEMON = "racoon"; @@ -34,11 +34,10 @@ class L2tpIpsecService extends VpnService<L2tpIpsecProfile> { // IPSEC AndroidServiceProxy ipsecService = startService(IPSEC_DAEMON); - ipsecService.sendCommand( - String.format("SETKEY %s %s", hostIp, serverIp)); - ipsecService.sendCommand(String.format("SET_CERTS %s %s %s %s", - serverIp, getCaCertPath(), getUserCertPath(), - getUserkeyPath())); + ipsecService.sendCommand(hostIp, serverIp, L2tpService.L2TP_PORT, + getUserkeyPath(), getUserCertPath(), getCaCertPath()); + + sleep(2000); // 2 seconds // L2TP L2tpIpsecProfile p = getProfile(); diff --git a/packages/VpnServices/src/com/android/server/vpn/MtpdHelper.java b/packages/VpnServices/src/com/android/server/vpn/MtpdHelper.java index 6160900..16d253a 100644 --- a/packages/VpnServices/src/com/android/server/vpn/MtpdHelper.java +++ b/packages/VpnServices/src/com/android/server/vpn/MtpdHelper.java @@ -38,7 +38,7 @@ class MtpdHelper { addPppArguments(vpnService, args, serverIp, username, password); AndroidServiceProxy mtpd = vpnService.startService(MTPD_SERVICE); - mtpd.sendCommand2(args.toArray(new String[args.size()])); + mtpd.sendCommand(args.toArray(new String[args.size()])); } private static void addPppArguments(VpnService<?> vpnService, diff --git a/packages/VpnServices/src/com/android/server/vpn/VpnService.java b/packages/VpnServices/src/com/android/server/vpn/VpnService.java index 44127ff..6e5d46b 100644 --- a/packages/VpnServices/src/com/android/server/vpn/VpnService.java +++ b/packages/VpnServices/src/com/android/server/vpn/VpnService.java @@ -24,14 +24,12 @@ import android.net.NetworkUtils; import android.net.vpn.VpnManager; import android.net.vpn.VpnProfile; import android.net.vpn.VpnState; -import android.os.FileObserver; import android.os.SystemProperties; +import android.text.TextUtils; import android.util.Log; -import java.io.File; import java.io.IOException; import java.net.InetAddress; -import java.net.InetSocketAddress; import java.net.NetworkInterface; import java.net.Socket; import java.util.ArrayList; @@ -43,21 +41,18 @@ import java.util.List; */ abstract class VpnService<E extends VpnProfile> { private static final int NOTIFICATION_ID = 1; - private static final String PROFILES_ROOT = VpnManager.PROFILES_PATH + "/"; - public static final String DEFAULT_CONFIG_PATH = "/etc"; - private static final int DNS_TIMEOUT = 3000; // ms private static final String DNS1 = "net.dns1"; private static final String DNS2 = "net.dns2"; + private static final String VPN_DNS1 = "vpn.dns1"; + private static final String VPN_DNS2 = "vpn.dns2"; + private static final String VPN_UP = "vpn.up"; + private static final String VPN_IS_UP = "1"; + private static final String VPN_IS_DOWN = "0"; + private static final String REMOTE_IP = "net.ipremote"; private static final String DNS_DOMAIN_SUFFICES = "net.dns.search"; - private static final String SERVER_IP = "net.vpn.server_ip"; - - private static final int VPN_TIMEOUT = 30000; // milliseconds - private static final int ONE_SECOND = 1000; // milliseconds - private static final int FIVE_SECOND = 5000; // milliseconds - private static final String LOGWRAPPER = "/system/bin/logwrapper"; private final String TAG = VpnService.class.getSimpleName(); E mProfile; @@ -76,13 +71,6 @@ abstract class VpnService<E extends VpnProfile> { private long mStartTime; // VPN connection start time - // monitors if the VPN connection is sucessfully established - private FileMonitor mConnectMonitor; - - // watch dog timer; fired up if the connection cannot be established within - // VPN_TIMEOUT - private Object mWatchdog; - // for helping managing multiple Android services private ServiceHelper mServiceHelper = new ServiceHelper(); @@ -110,19 +98,6 @@ abstract class VpnService<E extends VpnProfile> { return mServiceHelper.startService(serviceName); } - protected String getPppOptionFilePath() throws IOException { - String subpath = getProfileSubpath("/ppp/peers"); - String[] kids = new File(subpath).list(); - if ((kids == null) || (kids.length == 0)) { - throw new IOException("no option file found in " + subpath); - } - if (kids.length > 1) { - Log.w(TAG, "more than one option file found in " + subpath - + ", arbitrarily choose " + kids[0]); - } - return subpath + "/" + kids[0]; - } - /** * Returns the VPN profile associated with the connection. */ @@ -131,23 +106,6 @@ abstract class VpnService<E extends VpnProfile> { } /** - * Returns the profile path where configuration files reside. - */ - protected String getProfilePath() throws IOException { - String path = PROFILES_ROOT + mProfile.getId(); - File dir = new File(path); - if (!dir.exists()) throw new IOException("Profile dir does not exist"); - return path; - } - - /** - * Returns the path where default configuration files reside. - */ - protected String getDefaultConfigPath() throws IOException { - return DEFAULT_CONFIG_PATH; - } - - /** * Returns the host IP for establishing the VPN connection. */ protected String getHostIp() throws IOException { @@ -178,14 +136,6 @@ abstract class VpnService<E extends VpnProfile> { } /** - * Returns the path of the script file that is executed when the VPN - * connection is established. - */ - protected String getConnectMonitorFile() { - return "/etc/ppp/ip-up-vpn"; - } - - /** * Sets the system property. The method is blocked until the value is * settled in. * @param name the name of the property @@ -222,10 +172,10 @@ abstract class VpnService<E extends VpnProfile> { broadcastConnectivity(VpnState.CONNECTING); String serverIp = getIp(getProfile().getServerName()); - setSystemProperty(SERVER_IP, serverIp); - onBeforeConnect(); + onBeforeConnect(); connect(serverIp, username, password); + waitUntilConnectedOrTimedout(); } synchronized void onDisconnect(boolean cleanUpServices) { @@ -259,39 +209,36 @@ abstract class VpnService<E extends VpnProfile> { } } - private void createConnectMonitor() { - mConnectMonitor = new FileMonitor(getConnectMonitorFile(), - new Runnable() { - public void run() { - onConnectMonitorTriggered(); - } - }); - } - private void onBeforeConnect() { mNotification.disableNotification(); - createConnectMonitor(); - mConnectMonitor.startWatching(); - saveOriginalDnsProperties(); + SystemProperties.set(VPN_DNS1, "-"); + SystemProperties.set(VPN_DNS2, "-"); + SystemProperties.set(VPN_UP, VPN_IS_DOWN); + Log.d(TAG, " VPN UP: " + SystemProperties.get(VPN_UP)); + } - mWatchdog = startTimer(VPN_TIMEOUT, new Runnable() { - public void run() { - synchronized (VpnService.this) { - if (mState == VpnState.CONNECTING) { - Log.d(TAG, " watchdog timer is fired !!"); - onError(); - } - } + private void waitUntilConnectedOrTimedout() { + sleep(2000); // 2 seconds + for (int i = 0; i < 60; i++) { + if (VPN_IS_UP.equals(SystemProperties.get(VPN_UP))) { + onConnected(); + return; + } + sleep(500); // 0.5 second + } + + synchronized (this) { + if (mState == VpnState.CONNECTING) { + Log.d(TAG, " connecting timed out !!"); + onError(); } - }); + } } - private synchronized void onConnectMonitorTriggered() { - Log.d(TAG, "onConnectMonitorTriggered()"); + private synchronized void onConnected() { + Log.d(TAG, "onConnected()"); - stopTimer(mWatchdog); - mConnectMonitor.stopWatching(); saveVpnDnsProperties(); saveAndSetDomainSuffices(); startConnectivityMonitor(); @@ -310,8 +257,6 @@ abstract class VpnService<E extends VpnProfile> { restoreOriginalDnsProperties(); restoreOriginalDomainSuffices(); - if (mConnectMonitor != null) mConnectMonitor.stopWatching(); - if (mWatchdog != null) stopTimer(mWatchdog); mState = VpnState.IDLE; broadcastConnectivity(VpnState.IDLE); @@ -345,13 +290,6 @@ abstract class VpnService<E extends VpnProfile> { } } - private void saveOriginalDnsProperties() { - mOriginalDns1 = SystemProperties.get(DNS1); - mOriginalDns2 = SystemProperties.get(DNS2); - Log.d(TAG, String.format("save original dns prop: %s, %s", - mOriginalDns1, mOriginalDns2)); - } - private void restoreOriginalDnsProperties() { // restore only if they are not overridden if (mVpnDns1.equals(SystemProperties.get(DNS1))) { @@ -365,15 +303,21 @@ abstract class VpnService<E extends VpnProfile> { } private void saveVpnDnsProperties() { - mVpnDns1 = mVpnDns2 = ""; + mOriginalDns1 = mOriginalDns2 = ""; for (int i = 0; i < 10; i++) { - mVpnDns1 = SystemProperties.get(DNS1); - mVpnDns2 = SystemProperties.get(DNS2); - if (mVpnDns1.equals(mOriginalDns1)) { + mVpnDns1 = SystemProperties.get(VPN_DNS1); + mVpnDns2 = SystemProperties.get(VPN_DNS2); + if (mOriginalDns1.equals(mVpnDns1)) { Log.d(TAG, "wait for vpn dns to settle in..." + i); sleep(500); } else { - Log.d(TAG, String.format("save vpn dns prop: %s, %s", + mOriginalDns1 = SystemProperties.get(DNS1); + mOriginalDns2 = SystemProperties.get(DNS2); + SystemProperties.set(DNS1, mVpnDns1); + SystemProperties.set(DNS2, mVpnDns2); + Log.d(TAG, String.format("save original dns prop: %s, %s", + mOriginalDns1, mOriginalDns2)); + Log.d(TAG, String.format("set vpn dns prop: %s, %s", mVpnDns1, mVpnDns2)); return; } @@ -381,23 +325,11 @@ abstract class VpnService<E extends VpnProfile> { Log.e(TAG, "saveVpnDnsProperties(): DNS not updated??"); } - private void restoreVpnDnsProperties() { - if (isNullOrEmpty(mVpnDns1) && isNullOrEmpty(mVpnDns2)) { - return; - } - Log.d(TAG, String.format("restore vpn dns prop: %s --> %s", - SystemProperties.get(DNS1), mVpnDns1)); - Log.d(TAG, String.format("restore vpn dns prop: %s --> %s", - SystemProperties.get(DNS2), mVpnDns2)); - SystemProperties.set(DNS1, mVpnDns1); - SystemProperties.set(DNS2, mVpnDns2); - } - private void saveAndSetDomainSuffices() { mOriginalDomainSuffices = SystemProperties.get(DNS_DOMAIN_SUFFICES); Log.d(TAG, "save original dns search: " + mOriginalDomainSuffices); String list = mProfile.getDomainSuffices(); - if (!isNullOrEmpty(list)) { + if (!TextUtils.isEmpty(list)) { SystemProperties.set(DNS_DOMAIN_SUFFICES, list); } } @@ -423,7 +355,7 @@ abstract class VpnService<E extends VpnProfile> { if (mState != VpnState.CONNECTED) break; mNotification.update(); checkConnectivity(); - VpnService.this.wait(ONE_SECOND); + VpnService.this.wait(1000); // 1 second } } } catch (InterruptedException e) { @@ -446,32 +378,6 @@ abstract class VpnService<E extends VpnProfile> { } } - private Object startTimer(final int milliseconds, final Runnable task) { - Thread thread = new Thread(new Runnable() { - public void run() { - Log.d(TAG, "watchdog timer started"); - Thread t = Thread.currentThread(); - try { - synchronized (t) { - t.wait(milliseconds); - } - task.run(); - } catch (InterruptedException e) { - // ignored - } - Log.d(TAG, "watchdog timer stopped"); - } - }); - thread.start(); - return thread; - } - - private void stopTimer(Object timer) { - synchronized (timer) { - timer.notify(); - } - } - private String reallyGetHostIp() throws IOException { Enumeration<NetworkInterface> ifces = NetworkInterface.getNetworkInterfaces(); @@ -487,33 +393,13 @@ abstract class VpnService<E extends VpnProfile> { throw new IOException("Host IP is not available"); } - private String getProfileSubpath(String subpath) throws IOException { - String path = getProfilePath() + subpath; - if (new File(path).exists()) { - return path; - } else { - Log.w(TAG, "Profile subpath does not exist: " + path - + ", use default one"); - String path2 = getDefaultConfigPath() + subpath; - if (!new File(path2).exists()) { - throw new IOException("Profile subpath does not exist at " - + path + " or " + path2); - } - return path2; - } - } - - private void sleep(int ms) { + protected void sleep(int ms) { try { Thread.currentThread().sleep(ms); } catch (InterruptedException e) { } } - private static boolean isNullOrEmpty(String message) { - return ((message == null) || (message.length() == 0)); - } - private InetAddress toInetAddress(int addr) throws IOException { byte[] aa = new byte[4]; for (int i= 0; i < aa.length; i++) { @@ -564,20 +450,6 @@ abstract class VpnService<E extends VpnProfile> { } } - private class FileMonitor extends FileObserver { - private Runnable mCallback; - - FileMonitor(String path, Runnable callback) { - super(path, CLOSE_NOWRITE); - mCallback = callback; - } - - @Override - public void onEvent(int event, String path) { - if ((event & CLOSE_NOWRITE) > 0) mCallback.run(); - } - } - // Helper class for showing, updating notification. private class NotificationHelper { void update() { diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java index ecdf926..b15c06b 100644 --- a/services/java/com/android/server/BackupManagerService.java +++ b/services/java/com/android/server/BackupManagerService.java @@ -33,6 +33,7 @@ import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.PackageManager; import android.content.pm.Signature; import android.net.Uri; +import android.provider.Settings; import android.os.Binder; import android.os.Bundle; import android.os.Environment; @@ -42,7 +43,6 @@ import android.os.Message; import android.os.ParcelFileDescriptor; import android.os.Process; import android.os.RemoteException; -import android.os.SystemProperties; import android.util.Log; import android.util.SparseArray; @@ -74,12 +74,13 @@ class BackupManagerService extends IBackupManager.Stub { private static final String TAG = "BackupManagerService"; private static final boolean DEBUG = true; - // Persistent properties - private static final String BACKUP_TRANSPORT_PROPERTY = "persist.service.bkup.trans"; - private static final String BACKUP_ENABLED_PROPERTY = "persist.service.bkup.enabled"; + // Secure settings + private static final String BACKUP_TRANSPORT_SETTING = "backup_transport"; + private static final String BACKUP_ENABLED_SETTING = "backup_enabled"; - // Default time to wait after data changes before we back up the data - private static final long COLLECTION_INTERVAL = 3 * 60 * 1000; + // How often we perform a backup pass. Privileged external callers can + // trigger an immediate pass. + private static final long BACKUP_INTERVAL = 60 * 60 * 1000; private static final int MSG_RUN_BACKUP = 1; private static final int MSG_RUN_FULL_BACKUP = 2; @@ -166,7 +167,8 @@ class BackupManagerService extends IBackupManager.Stub { // Set up our bookkeeping // !!! STOPSHIP: make this disabled by default so that we then gate on // setupwizard or other opt-out UI - mEnabled = SystemProperties.getBoolean(BACKUP_ENABLED_PROPERTY, true); + mEnabled = (Settings.Secure.getInt(mContext.getContentResolver(), + BACKUP_ENABLED_SETTING, 1) != 0); mBaseStateDir = new File(Environment.getDataDirectory(), "backup"); mDataDir = Environment.getDownloadCacheDirectory(); @@ -191,8 +193,13 @@ class BackupManagerService extends IBackupManager.Stub { mGoogleTransport = null; // !!! TODO: set up the default transport name "the right way" - mCurrentTransport = SystemProperties.get(BACKUP_TRANSPORT_PROPERTY, - "com.google.android.backup/.BackupTransportService"); + mCurrentTransport = Settings.Secure.getString(mContext.getContentResolver(), + BACKUP_TRANSPORT_SETTING); + if (mCurrentTransport == null) { + mCurrentTransport = "com.google.android.backup/.BackupTransportService"; + Settings.Secure.putString(mContext.getContentResolver(), + BACKUP_TRANSPORT_SETTING, mCurrentTransport); + } if (DEBUG) Log.v(TAG, "Starting with transport " + mCurrentTransport); // Attach to the Google backup transport. When this comes up, it will set @@ -204,7 +211,7 @@ class BackupManagerService extends IBackupManager.Stub { context.bindService(intent, mGoogleConnection, Context.BIND_AUTO_CREATE); // Now that we know about valid backup participants, parse any - // leftover journal files and schedule a new backup pass + // leftover journal files into the pending backup set parseLeftoverJournals(); // Register for broadcasts about package install, etc., so we can @@ -214,7 +221,13 @@ class BackupManagerService extends IBackupManager.Stub { filter.addAction(Intent.ACTION_PACKAGE_REMOVED); filter.addDataScheme("package"); mContext.registerReceiver(mBroadcastReceiver, filter); - } + + // Schedule the first backup pass -- okay because no other threads are + // running yet + if (mEnabled) { + scheduleBackupPassLocked(BACKUP_INTERVAL); + } +} private void makeJournalLocked() { try { @@ -336,35 +349,39 @@ class BackupManagerService extends IBackupManager.Stub { ArrayList<BackupRequest> queue = new ArrayList<BackupRequest>(); File oldJournal = mJournal; synchronized (mQueueLock) { - if (mPendingBackups.size() == 0) { - Log.v(TAG, "Backup requested but nothing pending"); - break; - } - - for (BackupRequest b: mPendingBackups.values()) { - queue.add(b); - } - Log.v(TAG, "clearing pending backups"); - mPendingBackups.clear(); - - // Start a new backup-queue journal file too - if (mJournalStream != null) { - try { - mJournalStream.close(); - } catch (IOException e) { - // don't need to do anything + // Do we have any work to do? + if (mPendingBackups.size() > 0) { + for (BackupRequest b: mPendingBackups.values()) { + queue.add(b); + } + Log.v(TAG, "clearing pending backups"); + mPendingBackups.clear(); + + // Start a new backup-queue journal file too + if (mJournalStream != null) { + try { + mJournalStream.close(); + } catch (IOException e) { + // don't need to do anything + } + makeJournalLocked(); } - makeJournalLocked(); - } - // At this point, we have started a new journal file, and the old - // file identity is being passed to the backup processing thread. - // When it completes successfully, that old journal file will be - // deleted. If we crash prior to that, the old journal is parsed - // at next boot and the journaled requests fulfilled. + // At this point, we have started a new journal file, and the old + // file identity is being passed to the backup processing thread. + // When it completes successfully, that old journal file will be + // deleted. If we crash prior to that, the old journal is parsed + // at next boot and the journaled requests fulfilled. + (new PerformBackupThread(transport, queue, oldJournal)).start(); + } else { + Log.v(TAG, "Backup requested but nothing pending"); + } } - (new PerformBackupThread(transport, queue, oldJournal)).start(); + // Schedule the next pass. + synchronized (mQueueLock) { + scheduleBackupPassLocked(BACKUP_INTERVAL); + } break; } @@ -1109,10 +1126,6 @@ class BackupManagerService extends IBackupManager.Stub { Log.d(TAG, " + " + b + " agent=" + b.appInfo.backupAgentName); } } - // Schedule a backup pass in a few minutes. As backup-eligible data - // keeps changing, continue to defer the backup pass until things - // settle down, to avoid extra overhead. - scheduleBackupPassLocked(COLLECTION_INTERVAL); } } else { Log.w(TAG, "dataChanged but no participant pkg " + packageName); @@ -1148,20 +1161,21 @@ class BackupManagerService extends IBackupManager.Stub { boolean wasEnabled = mEnabled; synchronized (this) { - SystemProperties.set(BACKUP_ENABLED_PROPERTY, enable ? "true" : "false"); + Settings.Secure.putInt(mContext.getContentResolver(), BACKUP_ENABLED_SETTING, + enable ? 1 : 0); mEnabled = enable; } - if (enable && !wasEnabled) { - synchronized (mQueueLock) { - if (mPendingBackups.size() > 0) { - // !!! TODO: better policy around timing of the first backup pass - if (DEBUG) Log.v(TAG, "Backup enabled with pending data changes, scheduling"); - this.scheduleBackupPassLocked(COLLECTION_INTERVAL); - } + synchronized (mQueueLock) { + if (enable && !wasEnabled) { + // if we've just been enabled, start scheduling backup passes + scheduleBackupPassLocked(BACKUP_INTERVAL); + } else if (!enable) { + // No longer enabled, so stop running backups. + mBackupHandler.removeMessages(MSG_RUN_BACKUP); } } -} + } // Report whether the backup mechanism is currently enabled public boolean isBackupEnabled() { @@ -1206,7 +1220,8 @@ class BackupManagerService extends IBackupManager.Stub { if (mTransports.get(transport) != null) { prevTransport = mCurrentTransport; mCurrentTransport = transport; - SystemProperties.set(BACKUP_TRANSPORT_PROPERTY, transport); + Settings.Secure.putString(mContext.getContentResolver(), BACKUP_TRANSPORT_SETTING, + transport); Log.v(TAG, "selectBackupTransport() set " + mCurrentTransport + " returning " + prevTransport); } else { @@ -1334,13 +1349,22 @@ class BackupManagerService extends IBackupManager.Stub { @Override public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { synchronized (mQueueLock) { + pw.println("Backup Manager is " + (mEnabled ? "enabled" : "disabled")); + boolean scheduled = mBackupHandler.hasMessages(MSG_RUN_BACKUP); + if (scheduled != mEnabled) { + if (mEnabled) { + pw.println("ERROR: backups enabled but none scheduled!"); + } else { + pw.println("ERROR: backups are scheduled but not enabled!"); + } + } pw.println("Available transports:"); for (String t : listAllTransports()) { String pad = (t.equals(mCurrentTransport)) ? " * " : " "; pw.println(pad + t); } int N = mBackupParticipants.size(); - pw.println("Participants:"); + pw.println("Participants: " + N); for (int i=0; i<N; i++) { int uid = mBackupParticipants.keyAt(i); pw.print(" uid: "); |
