diff options
25 files changed, 1052 insertions, 268 deletions
diff --git a/api/current.xml b/api/current.xml index cb348e16295e..f30c505f27b5 100644 --- a/api/current.xml +++ b/api/current.xml @@ -3518,17 +3518,6 @@ visibility="public" > </field> -<field name="donut_resource_pad30" - type="int" - transient="false" - volatile="false" - value="16843394" - static="true" - final="true" - deprecated="not deprecated" - visibility="public" -> -</field> <field name="donut_resource_pad4" type="int" transient="false" @@ -6840,6 +6829,17 @@ visibility="public" > </field> +<field name="queryAfterZeroResults" + type="int" + transient="false" + volatile="false" + value="16843394" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="radioButtonStyle" type="int" transient="false" @@ -62837,7 +62837,7 @@ type="float" transient="false" volatile="false" - value="0.0010f" + value="0.001f" static="true" final="true" deprecated="not deprecated" diff --git a/cmds/keystore/commands.c b/cmds/keystore/commands.c index 7474d81a35f1..e53cecef2bdb 100644 --- a/cmds/keystore/commands.c +++ b/cmds/keystore/commands.c @@ -40,7 +40,7 @@ static int list_files(const char *dir, char reply[REPLY_MAX]) { reply[0]=0; while ((de = readdir(d))) { if (de->d_type != DT_REG) continue; - strlcat(reply, " ", REPLY_MAX); + if (reply[0] != 0) strlcat(reply, " ", REPLY_MAX); if (strlcat(reply, de->d_name, REPLY_MAX) >= REPLY_MAX) { LOGE("reply is too long(too many files under '%s'\n", dir); return -1; diff --git a/cmds/keystore/keystore.c b/cmds/keystore/keystore.c index dbb62b37a8b7..5193b3dba5de 100644 --- a/cmds/keystore/keystore.c +++ b/cmds/keystore/keystore.c @@ -113,7 +113,7 @@ static int execute(int s, char cmd[BUFFER_MAX]) unsigned i; unsigned n = 0; unsigned short count; - int ret = -1; + short ret = -1; /* default reply is "" */ reply[0] = 0; @@ -139,7 +139,7 @@ static int execute(int s, char cmd[BUFFER_MAX]) LOGE("%s requires %d arguments (%d given)\n", cmds[i].name, cmds[i].numargs, n); } else { - ret = cmds[i].func(arg + 1, reply); + ret = (short) cmds[i].func(arg + 1, reply); } goto done; } @@ -148,24 +148,26 @@ static int execute(int s, char cmd[BUFFER_MAX]) done: if (reply[0]) { - n = snprintf(cmd, BUFFER_MAX, "%d %s", ret, reply); + strlcpy(cmd, reply, BUFFER_MAX); + count = strlen(cmd); } else { - n = snprintf(cmd, BUFFER_MAX, "%d", ret); + count = 0; + } + if (writex(s, &ret, sizeof(ret))) return -1; + if (ret == 0) { + if (writex(s, &count, sizeof(count))) return -1; + if (writex(s, cmd, count)) return -1; } - if (n > BUFFER_MAX) n = BUFFER_MAX; - count = n; - - if (writex(s, &count, sizeof(count))) return -1; - if (writex(s, cmd, count)) return -1; return 0; } int shell_command(const int argc, const char **argv) { - int fd, i, r; + int fd, i; + short ret; unsigned short count; - char cmd[BUFFER_MAX]=""; + char buf[BUFFER_MAX]=""; fd = socket_local_client(SOCKET_PATH, ANDROID_SOCKET_NAMESPACE_RESERVED, @@ -175,19 +177,24 @@ int shell_command(const int argc, const char **argv) exit(1); } for(i = 0; i < argc; i++) { - if (i > 0) strlcat(cmd, " ", BUFFER_MAX); - if(strlcat(cmd, argv[i], BUFFER_MAX) >= BUFFER_MAX) { + if (i > 0) strlcat(buf, " ", BUFFER_MAX); + if(strlcat(buf, argv[i], BUFFER_MAX) >= BUFFER_MAX) { fprintf(stderr, "Arguments are too long\n"); exit(1); } } - count = strlen(cmd); + count = strlen(buf); if (writex(fd, &count, sizeof(count))) return -1; - if (writex(fd, cmd, strlen(cmd))) return -1; - if (readx(fd, &count, sizeof(count))) return -1; - if (readx(fd, cmd, count)) return -1; - cmd[count]=0; - fprintf(stdout, "%s\n", cmd); + if (writex(fd, buf, strlen(buf))) return -1; + if (readx(fd, &ret, sizeof(ret))) return -1; + if (ret == 0) { + if (readx(fd, &count, sizeof(count))) return -1; + if (readx(fd, buf, count)) return -1; + buf[count]=0; + fprintf(stdout, "%s\n", buf); + } else { + fprintf(stderr, "Failed, please check log!\n"); + } return 0; } diff --git a/core/java/android/app/SearchManager.java b/core/java/android/app/SearchManager.java index 3f0aa950e19c..820f192cd12d 100644 --- a/core/java/android/app/SearchManager.java +++ b/core/java/android/app/SearchManager.java @@ -1402,6 +1402,16 @@ public class SearchManager * @hide For internal use, not part of the public API. */ public final static String SUGGEST_COLUMN_BACKGROUND_COLOR = "suggest_background_color"; + + /** + * Column name for suggestions cursor. <i>Optional.</i> This column is used to specify + * that a spinner should be shown in lieu of an icon2 while the shortcut of this suggestion + * is being refreshed. + * + * @hide Pending API council approval. + */ + public final static String SUGGEST_COLUMN_SPINNER_WHILE_REFRESHING = + "suggest_spinner_while_refreshing"; /** * Column value for suggestion column {@link #SUGGEST_COLUMN_SHORTCUT_ID} when a suggestion diff --git a/core/java/android/app/SuggestionsAdapter.java b/core/java/android/app/SuggestionsAdapter.java index 6e8bd78b7b46..8c5cee3e0109 100644 --- a/core/java/android/app/SuggestionsAdapter.java +++ b/core/java/android/app/SuggestionsAdapter.java @@ -20,16 +20,19 @@ import android.content.ContentResolver; import android.content.Context; import android.content.res.Resources.NotFoundException; import android.database.Cursor; +import android.graphics.Canvas; import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Bundle; import android.server.search.SearchableInfo; import android.text.Html; import android.text.TextUtils; +import android.util.DisplayMetrics; import android.util.Log; import android.util.TypedValue; import android.view.View; import android.view.ViewGroup; +import android.widget.AbsListView; import android.widget.ImageView; import android.widget.ResourceCursorAdapter; import android.widget.TextView; @@ -255,7 +258,7 @@ class SuggestionsAdapter extends ResourceCursorAdapter { */ @Override public View newView(Context context, Cursor cursor, ViewGroup parent) { - View v = super.newView(context, cursor, parent); + View v = new SuggestionItemView(context, cursor); v.setTag(new ChildViewCache(v)); return v; } @@ -303,7 +306,7 @@ class SuggestionsAdapter extends ResourceCursorAdapter { if (backgroundColor == 0) { backgroundColor = mDefaultBackgroundColor; } - view.setBackgroundColor(backgroundColor); + ((SuggestionItemView)view).setColor(backgroundColor); final boolean isHtml = mFormatCol > 0 && "html".equals(cursor.getString(mFormatCol)); setViewText(cursor, views.mText1, mText1Col, isHtml); @@ -506,4 +509,68 @@ class SuggestionsAdapter extends ResourceCursorAdapter { return cursor.getString(col); } + /** + * A parent viewgroup class which holds the actual suggestion item as a child. + * + * The sole purpose of this class is to draw the given background color when the item is in + * normal state and not draw the background color when it is pressed, so that when pressed the + * list view's selection highlight will be displayed properly (if we draw our background it + * draws on top of the list view selection highlight). + */ + private class SuggestionItemView extends ViewGroup { + private int mBackgroundColor; // the background color to draw in normal state. + private View mView; // the suggestion item's view. + + protected SuggestionItemView(Context context, Cursor cursor) { + // Initialize ourselves + super(context); + + // For our layout use the default list item height from the current theme. + TypedValue lineHeight = new TypedValue(); + context.getTheme().resolveAttribute( + com.android.internal.R.attr.searchResultListItemHeight, lineHeight, true); + DisplayMetrics metrics = new DisplayMetrics(); + metrics.setToDefaults(); + AbsListView.LayoutParams layout = new AbsListView.LayoutParams( + AbsListView.LayoutParams.FILL_PARENT, + (int)lineHeight.getDimension(metrics)); + + setLayoutParams(layout); + + // Initialize the child view + mView = SuggestionsAdapter.super.newView(context, cursor, this); + if (mView != null) { + addView(mView, layout.width, layout.height); + mView.setVisibility(View.VISIBLE); + } + } + + public void setColor(int backgroundColor) { + mBackgroundColor = backgroundColor; + } + + @Override + public void dispatchDraw(Canvas canvas) { + if (!isPressed()) { + canvas.drawColor(mBackgroundColor); + } + super.dispatchDraw(canvas); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + if (mView != null) { + mView.measure(widthMeasureSpec, heightMeasureSpec); + } + } + + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + if (mView != null) { + mView.layout(0, 0, mView.getMeasuredWidth(), mView.getMeasuredHeight()); + } + } + } + } diff --git a/core/java/android/backup/BackupManager.java b/core/java/android/backup/BackupManager.java index c3b6a02e7516..8df7eae3a545 100644 --- a/core/java/android/backup/BackupManager.java +++ b/core/java/android/backup/BackupManager.java @@ -45,7 +45,7 @@ public class BackupManager { /** * Defined backup transports understood by {@link IBackupManager.selectBackupTransport}. */ - public static final int TRANSPORT_ADB = 1; + public static final int TRANSPORT_LOCAL = 1; public static final int TRANSPORT_GOOGLE = 2; /** diff --git a/core/java/android/server/search/SearchableInfo.java b/core/java/android/server/search/SearchableInfo.java index c08314272d50..4df736814151 100644 --- a/core/java/android/server/search/SearchableInfo.java +++ b/core/java/android/server/search/SearchableInfo.java @@ -66,6 +66,7 @@ public final class SearchableInfo implements Parcelable { private final int mSearchInputType; private final int mSearchImeOptions; private final boolean mIncludeInGlobalSearch; + private final boolean mQueryAfterZeroResults; private final String mSuggestAuthority; private final String mSuggestPath; private final String mSuggestSelection; @@ -276,6 +277,8 @@ public final class SearchableInfo implements Parcelable { EditorInfo.IME_ACTION_SEARCH); mIncludeInGlobalSearch = a.getBoolean( com.android.internal.R.styleable.Searchable_includeInGlobalSearch, false); + mQueryAfterZeroResults = a.getBoolean( + com.android.internal.R.styleable.Searchable_queryAfterZeroResults, false); mSuggestAuthority = a.getString( com.android.internal.R.styleable.Searchable_searchSuggestAuthority); @@ -637,6 +640,17 @@ public final class SearchableInfo implements Parcelable { } /** + * Checks whether this searchable activity should be invoked after a query returned zero + * results. + * + * @return The value of the <code>queryAfterZeroResults</code> attribute, + * or <code>false</code> if the attribute is not set. + */ + public boolean queryAfterZeroResults() { + return mQueryAfterZeroResults; + } + + /** * Support for parcelable and aidl operations. */ public static final Parcelable.Creator<SearchableInfo> CREATOR @@ -667,6 +681,7 @@ public final class SearchableInfo implements Parcelable { mSearchInputType = in.readInt(); mSearchImeOptions = in.readInt(); mIncludeInGlobalSearch = in.readInt() != 0; + mQueryAfterZeroResults = in.readInt() != 0; mSuggestAuthority = in.readString(); mSuggestPath = in.readString(); @@ -702,6 +717,7 @@ public final class SearchableInfo implements Parcelable { dest.writeInt(mSearchInputType); dest.writeInt(mSearchImeOptions); dest.writeInt(mIncludeInGlobalSearch ? 1 : 0); + dest.writeInt(mQueryAfterZeroResults ? 1 : 0); dest.writeString(mSuggestAuthority); dest.writeString(mSuggestPath); diff --git a/core/java/android/widget/AutoCompleteTextView.java b/core/java/android/widget/AutoCompleteTextView.java index f376ce5ae107..585ce3d2f08a 100644 --- a/core/java/android/widget/AutoCompleteTextView.java +++ b/core/java/android/widget/AutoCompleteTextView.java @@ -123,10 +123,6 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe private AutoCompleteTextView.ListSelectorHider mHideSelector; - // Indicates whether this AutoCompleteTextView is attached to a window or not - // The widget is attached to a window when mAttachCount > 0 - private int mAttachCount; - private AutoCompleteTextView.PassThroughClickListener mPassThroughClickListener; public AutoCompleteTextView(Context context) { @@ -960,7 +956,8 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe /** {@inheritDoc} */ public void onFilterComplete(int count) { - if (mAttachCount <= 0) return; + // Not attached to window, don't update drop-down + if (getWindowVisibility() == View.GONE) return; /* * This checks enoughToFilter() again because filtering requests @@ -999,13 +996,11 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); - mAttachCount++; } @Override protected void onDetachedFromWindow() { dismissDropDown(); - mAttachCount--; super.onDetachedFromWindow(); } diff --git a/core/java/com/android/internal/backup/AdbTransport.java b/core/java/com/android/internal/backup/AdbTransport.java deleted file mode 100644 index 8d3bd1c4cb17..000000000000 --- a/core/java/com/android/internal/backup/AdbTransport.java +++ /dev/null @@ -1,55 +0,0 @@ -package com.android.internal.backup; - -import android.backup.RestoreSet; -import android.content.pm.PackageInfo; -import android.os.ParcelFileDescriptor; -import android.os.RemoteException; - -/** - * Backup transport for full backup over adb. This transport pipes everything to - * a file in a known location in /cache, which 'adb backup' then pulls to the desktop - * (deleting it afterwards). - */ - -public class AdbTransport extends IBackupTransport.Stub { - - public long requestBackupTime() throws RemoteException { - return 0; - } - - public int startSession() throws RemoteException { - // TODO Auto-generated method stub - return 0; - } - - public int endSession() throws RemoteException { - // TODO Auto-generated method stub - return 0; - } - - public int performBackup(PackageInfo packageInfo, ParcelFileDescriptor data) - throws RemoteException { - // TODO Auto-generated method stub - return 0; - } - - // Restore handling - public RestoreSet[] getAvailableRestoreSets() throws android.os.RemoteException { - RestoreSet[] set = new RestoreSet[1]; - set[0].device = "USB"; - set[0].name = "adb"; - set[0].token = 0; - return set; - } - - public PackageInfo[] getAppSet(int token) throws android.os.RemoteException { - // !!! TODO: real implementation - return new PackageInfo[0]; - } - - public int getRestoreData(int token, PackageInfo packageInfo, ParcelFileDescriptor data) - throws android.os.RemoteException { - // !!! TODO: real implementation - return 0; - } -} diff --git a/core/java/com/android/internal/backup/LocalTransport.java b/core/java/com/android/internal/backup/LocalTransport.java new file mode 100644 index 000000000000..62fba4ac5295 --- /dev/null +++ b/core/java/com/android/internal/backup/LocalTransport.java @@ -0,0 +1,140 @@ +package com.android.internal.backup; + +import android.backup.RestoreSet; +import android.content.Context; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.os.Environment; +import android.os.ParcelFileDescriptor; +import android.os.RemoteException; +import android.util.Log; + +import java.io.File; +import java.io.FileFilter; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.ArrayList; + +/** + * Backup transport for stashing stuff into a known location on disk, and + * later restoring from there. For testing only. + */ + +public class LocalTransport extends IBackupTransport.Stub { + private static final String TAG = "LocalTransport"; + private static final String DATA_FILE_NAME = "data"; + + private Context mContext; + private PackageManager mPackageManager; + private File mDataDir = new File(Environment.getDownloadCacheDirectory(), "backup"); + private FileFilter mDirFileFilter = new FileFilter() { + public boolean accept(File f) { + return f.isDirectory(); + } + }; + + + public LocalTransport(Context context) { + mContext = context; + mPackageManager = context.getPackageManager(); + } + + public long requestBackupTime() throws RemoteException { + // any time is a good time for local backup + return 0; + } + + public int startSession() throws RemoteException { + return 0; + } + + public int endSession() throws RemoteException { + return 0; + } + + public int performBackup(PackageInfo packageInfo, ParcelFileDescriptor data) + throws RemoteException { + File packageDir = new File(mDataDir, packageInfo.packageName); + File imageFileName = new File(packageDir, DATA_FILE_NAME); + + //!!! TODO: process the (partial) update into the persistent restore set: + + // Parse out the existing image file into the key/value map + + // Parse out the backup data into the key/value updates + + // Apply the backup key/value updates to the image + + // Write out the image in the canonical format + + return -1; + } + + // Restore handling + public RestoreSet[] getAvailableRestoreSets() throws android.os.RemoteException { + // one hardcoded restore set + RestoreSet[] set = new RestoreSet[1]; + set[0].device = "flash"; + set[0].name = "Local disk image"; + set[0].token = 0; + return set; + } + + public PackageInfo[] getAppSet(int token) throws android.os.RemoteException { + // the available packages are the extant subdirs of mDatadir + File[] packageDirs = mDataDir.listFiles(mDirFileFilter); + ArrayList<PackageInfo> packages = new ArrayList<PackageInfo>(); + for (File dir : packageDirs) { + try { + PackageInfo pkg = mPackageManager.getPackageInfo(dir.getName(), + PackageManager.GET_SIGNATURES); + if (pkg != null) { + packages.add(pkg); + } + } catch (NameNotFoundException e) { + // restore set contains data for a package not installed on the + // phone -- just ignore it. + } + } + + Log.v(TAG, "Built app set of " + packages.size() + " entries:"); + for (PackageInfo p : packages) { + Log.v(TAG, " + " + p.packageName); + } + + PackageInfo[] result = new PackageInfo[packages.size()]; + return packages.toArray(result); + } + + public int getRestoreData(int token, PackageInfo packageInfo, ParcelFileDescriptor output) + throws android.os.RemoteException { + // we only support one hardcoded restore set + if (token != 0) return -1; + + // the data for a given package is at a known location + File packageDir = new File(mDataDir, packageInfo.packageName); + File imageFile = new File(packageDir, DATA_FILE_NAME); + + // restore is relatively easy: we already maintain the full data set in + // the canonical form understandable to the BackupAgent + return copyFileToFD(imageFile, output); + } + + private int copyFileToFD(File source, ParcelFileDescriptor dest) { + try { + FileInputStream in = new FileInputStream(source); + FileOutputStream out = new FileOutputStream(dest.getFileDescriptor()); + byte[] buffer = new byte[4096]; + int bytesRead; + while ((bytesRead = in.read(buffer)) >= 0) { + out.write(buffer, 0, bytesRead); + } + } catch (IOException e) { + // something went wrong; claim failure + return -1; + } + return 0; + } +} diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index 99a381c5ae13..16a3bad17bf1 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -30,9 +30,11 @@ import android.util.PrintWriterPrinter; import android.util.Printer; import android.util.SparseArray; +import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; +import java.io.FileReader; import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; @@ -53,7 +55,7 @@ public final class BatteryStatsImpl extends BatteryStats { private static final int MAGIC = 0xBA757475; // 'BATSTATS' // Current on-disk Parcel version - private static final int VERSION = 38; + private static final int VERSION = 39; private final File mFile; private final File mBackupFile; @@ -94,7 +96,7 @@ public final class BatteryStatsImpl extends BatteryStats { boolean mScreenOn; StopwatchTimer mScreenOnTimer; - + int mScreenBrightnessBin = -1; final StopwatchTimer[] mScreenBrightnessTimer = new StopwatchTimer[NUM_SCREEN_BRIGHTNESS_BINS]; @@ -137,10 +139,10 @@ public final class BatteryStatsImpl extends BatteryStats { long mTrackBatteryUptimeStart; long mTrackBatteryPastRealtime; long mTrackBatteryRealtimeStart; - + long mUnpluggedBatteryUptime; long mUnpluggedBatteryRealtime; - + /* * These keep track of battery levels (1-100) at the last plug event and the last unplug event. */ @@ -149,6 +151,15 @@ public final class BatteryStatsImpl extends BatteryStats { long mLastWriteTime = 0; // Milliseconds + // Mobile data transferred while on battery + private long[] mMobileDataTx = new long[4]; + private long[] mMobileDataRx = new long[4]; + private long[] mTotalDataTx = new long[4]; + private long[] mTotalDataRx = new long[4]; + + private long mRadioDataUptime; + private long mRadioDataStart; + /* * Holds a SamplingTimer associated with each kernel wakelock name being tracked. */ @@ -893,7 +904,40 @@ public final class BatteryStatsImpl extends BatteryStats { } return kwlt; } - + + private void doDataPlug(long[] dataTransfer, long currentBytes) { + dataTransfer[STATS_LAST] = dataTransfer[STATS_UNPLUGGED]; + dataTransfer[STATS_UNPLUGGED] = -1; + } + + private void doDataUnplug(long[] dataTransfer, long currentBytes) { + dataTransfer[STATS_UNPLUGGED] = currentBytes; + } + + private long getCurrentRadioDataUptimeMs() { + try { + File awakeTimeFile = new File("/sys/devices/virtual/net/rmnet0/awake_time_ms"); + if (!awakeTimeFile.exists()) return 0; + BufferedReader br = new BufferedReader(new FileReader(awakeTimeFile)); + String line = br.readLine(); + br.close(); + return Long.parseLong(line); + } catch (NumberFormatException nfe) { + // Nothing + } catch (IOException ioe) { + // Nothing + } + return 0; + } + + public long getRadioDataUptimeMs() { + if (mRadioDataStart == -1) { + return mRadioDataUptime; + } else { + return getCurrentRadioDataUptimeMs() - mRadioDataStart; + } + } + public void doUnplug(long batteryUptime, long batteryRealtime) { for (int iu = mUidStats.size() - 1; iu >= 0; iu--) { Uid u = mUidStats.valueAt(iu); @@ -905,8 +949,16 @@ public final class BatteryStatsImpl extends BatteryStats { for (int i = mUnpluggables.size() - 1; i >= 0; i--) { mUnpluggables.get(i).unplug(batteryUptime, batteryRealtime); } + // Track total mobile data + doDataUnplug(mMobileDataRx, NetStat.getMobileRxBytes()); + doDataUnplug(mMobileDataTx, NetStat.getMobileTxBytes()); + doDataUnplug(mTotalDataRx, NetStat.getTotalRxBytes()); + doDataUnplug(mTotalDataTx, NetStat.getTotalTxBytes()); + // Track radio awake time + mRadioDataStart = getCurrentRadioDataUptimeMs(); + mRadioDataUptime = 0; } - + public void doPlug(long batteryUptime, long batteryRealtime) { for (int iu = mUidStats.size() - 1; iu >= 0; iu--) { Uid u = mUidStats.valueAt(iu); @@ -922,8 +974,15 @@ public final class BatteryStatsImpl extends BatteryStats { for (int i = mUnpluggables.size() - 1; i >= 0; i--) { mUnpluggables.get(i).plug(batteryUptime, batteryRealtime); } + doDataPlug(mMobileDataRx, NetStat.getMobileRxBytes()); + doDataPlug(mMobileDataTx, NetStat.getMobileTxBytes()); + doDataPlug(mTotalDataRx, NetStat.getTotalRxBytes()); + doDataPlug(mTotalDataTx, NetStat.getTotalTxBytes()); + // Track radio awake time + mRadioDataUptime = getRadioDataUptimeMs(); + mRadioDataStart = -1; } - + public void noteStartGps(int uid) { mUidStats.get(uid).noteStartGps(); } @@ -931,7 +990,7 @@ public final class BatteryStatsImpl extends BatteryStats { public void noteStopGps(int uid) { mUidStats.get(uid).noteStopGps(); } - + public void noteScreenOnLocked() { if (!mScreenOn) { mScreenOn = true; @@ -1039,6 +1098,7 @@ public final class BatteryStatsImpl extends BatteryStats { break; } } + if (DEBUG) Log.i(TAG, "Phone Data Connection -> " + dataType + " = " + hasData); if (mPhoneDataConnectionType != bin) { if (mPhoneDataConnectionType >= 0) { mPhoneDataConnectionsTimer[mPhoneDataConnectionType].stopRunningLocked(this); @@ -2701,7 +2761,44 @@ public final class BatteryStatsImpl extends BatteryStats { public long getBatteryRealtime(long curTime) { return getBatteryRealtimeLocked(curTime); } - + + private long getTcpBytes(long current, long[] dataBytes, int which) { + if (which == STATS_LAST) { + return dataBytes[STATS_LAST]; + } else { + if (which == STATS_UNPLUGGED) { + if (dataBytes[STATS_UNPLUGGED] < 0) { + return dataBytes[STATS_LAST]; + } else { + return current - dataBytes[STATS_UNPLUGGED]; + } + } else if (which == STATS_TOTAL) { + return (current - dataBytes[STATS_CURRENT]) + dataBytes[STATS_TOTAL]; + } + return current - dataBytes[STATS_CURRENT]; + } + } + + /** Only STATS_UNPLUGGED works properly */ + public long getMobileTcpBytesSent(int which) { + return getTcpBytes(NetStat.getMobileTxBytes(), mMobileDataTx, which); + } + + /** Only STATS_UNPLUGGED works properly */ + public long getMobileTcpBytesReceived(int which) { + return getTcpBytes(NetStat.getMobileRxBytes(), mMobileDataRx, which); + } + + /** Only STATS_UNPLUGGED works properly */ + public long getTotalTcpBytesSent(int which) { + return getTcpBytes(NetStat.getTotalTxBytes(), mTotalDataTx, which); + } + + /** Only STATS_UNPLUGGED works properly */ + public long getTotalTcpBytesReceived(int which) { + return getTcpBytes(NetStat.getTotalRxBytes(), mTotalDataRx, which); + } + @Override public int getDischargeStartLevel() { synchronized(this) { @@ -3227,6 +3324,18 @@ public final class BatteryStatsImpl extends BatteryStats { mDischargeCurrentLevel = in.readInt(); mLastWriteTime = in.readLong(); + mMobileDataRx[STATS_LAST] = in.readLong(); + mMobileDataRx[STATS_UNPLUGGED] = -1; + mMobileDataTx[STATS_LAST] = in.readLong(); + mMobileDataTx[STATS_UNPLUGGED] = -1; + mTotalDataRx[STATS_LAST] = in.readLong(); + mTotalDataRx[STATS_UNPLUGGED] = -1; + mTotalDataTx[STATS_LAST] = in.readLong(); + mTotalDataTx[STATS_UNPLUGGED] = -1; + + mRadioDataUptime = in.readLong(); + mRadioDataStart = -1; + mKernelWakelockStats.clear(); int NKW = in.readInt(); for (int ikw = 0; ikw < NKW; ikw++) { @@ -3301,6 +3410,14 @@ public final class BatteryStatsImpl extends BatteryStats { out.writeInt(mDischargeCurrentLevel); out.writeLong(mLastWriteTime); + out.writeLong(getMobileTcpBytesReceived(STATS_UNPLUGGED)); + out.writeLong(getMobileTcpBytesSent(STATS_UNPLUGGED)); + out.writeLong(getTotalTcpBytesReceived(STATS_UNPLUGGED)); + out.writeLong(getTotalTcpBytesSent(STATS_UNPLUGGED)); + + // Write radio uptime for data + out.writeLong(getRadioDataUptimeMs()); + out.writeInt(mKernelWakelockStats.size()); for (Map.Entry<String, SamplingTimer> ent : mKernelWakelockStats.entrySet()) { SamplingTimer kwlt = ent.getValue(); diff --git a/core/java/com/android/internal/os/PowerProfile.java b/core/java/com/android/internal/os/PowerProfile.java index f08dddd47074..a37bf6e3f446 100644 --- a/core/java/com/android/internal/os/PowerProfile.java +++ b/core/java/com/android/internal/os/PowerProfile.java @@ -26,6 +26,7 @@ import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; +import java.util.ArrayList; import java.util.HashMap; /** @@ -118,26 +119,28 @@ public class PowerProfile { */ public static final String POWER_VIDEO = "dsp.video"; - static final HashMap<String, Double> sPowerMap = new HashMap<String, Double>(); + static final HashMap<String, Object> sPowerMap = new HashMap<String, Object>(); private static final String TAG_DEVICE = "device"; private static final String TAG_ITEM = "item"; + private static final String TAG_ARRAY = "array"; + private static final String TAG_ARRAYITEM = "value"; private static final String ATTR_NAME = "name"; - public PowerProfile(Context context, CharSequence profile) { + public PowerProfile(Context context) { // Read the XML file for the given profile (normally only one per // device) if (sPowerMap.size() == 0) { - readPowerValuesFromXml(context, profile); + readPowerValuesFromXml(context); } } - private void readPowerValuesFromXml(Context context, CharSequence profile) { - // FIXME - //int id = context.getResources().getIdentifier(profile.toString(), "xml", - // "com.android.internal"); - int id = com.android.internal.R.xml.power_profile_default; + private void readPowerValuesFromXml(Context context) { + int id = com.android.internal.R.xml.power_profile; XmlResourceParser parser = context.getResources().getXml(id); + boolean parsingArray = false; + ArrayList<Double> array = new ArrayList<Double>(); + String arrayName = null; try { XmlUtils.beginDocument(parser, TAG_DEVICE); @@ -145,22 +148,39 @@ public class PowerProfile { while (true) { XmlUtils.nextElement(parser); - String element = parser.getName(); - if (element == null || !(element.equals(TAG_ITEM))) { - break; + String element = parser.getName(); + if (element == null) break; + + if (parsingArray && !element.equals(TAG_ARRAYITEM)) { + // Finish array + sPowerMap.put(arrayName, array.toArray(new Double[array.size()])); + parsingArray = false; } - - String name = parser.getAttributeValue(null, ATTR_NAME); - if (parser.next() == XmlPullParser.TEXT) { - String power = parser.getText(); - double value = 0; - try { - value = Double.valueOf(power); - } catch (NumberFormatException nfe) { + if (element.equals(TAG_ARRAY)) { + parsingArray = true; + array.clear(); + arrayName = parser.getAttributeValue(null, ATTR_NAME); + } else if (element.equals(TAG_ITEM) || element.equals(TAG_ARRAYITEM)) { + String name = null; + if (!parsingArray) name = parser.getAttributeValue(null, ATTR_NAME); + if (parser.next() == XmlPullParser.TEXT) { + String power = parser.getText(); + double value = 0; + try { + value = Double.valueOf(power); + } catch (NumberFormatException nfe) { + } + if (element.equals(TAG_ITEM)) { + sPowerMap.put(name, value); + } else if (parsingArray) { + array.add(value); + } } - sPowerMap.put(name, value); } } + if (parsingArray) { + sPowerMap.put(arrayName, array.toArray(new Double[array.size()])); + } } catch (XmlPullParserException e) { throw new RuntimeException(e); } catch (IOException e) { @@ -177,7 +197,40 @@ public class PowerProfile { */ public double getAveragePower(String type) { if (sPowerMap.containsKey(type)) { - return sPowerMap.get(type); + Object data = sPowerMap.get(type); + if (data instanceof Double[]) { + return ((Double[])data)[0]; + } else { + return (Double) sPowerMap.get(type); + } + } else { + return 0; + } + } + + /** + * Returns the average current in mA consumed by the subsystem for the given level. + * @param type the subsystem type + * @param level the level of power at which the subsystem is running. For instance, the + * signal strength of the cell network between 0 and 4 (if there are 4 bars max.). + * If there is no data for multiple levels, the level is ignored. + * @return the average current in milliAmps. + */ + public double getAveragePower(String type, int level) { + if (sPowerMap.containsKey(type)) { + Object data = sPowerMap.get(type); + if (data instanceof double[]) { + final double[] values = (double[]) data; + if (values.length > level) { + return values[level]; + } else if (values.length < 0) { + return values[0]; + } else { + return values[values.length - 1]; + } + } else { + return (Double) data; + } } else { return 0; } diff --git a/core/jni/android_location_GpsLocationProvider.cpp b/core/jni/android_location_GpsLocationProvider.cpp index 08587411f524..5c4fb229634e 100644 --- a/core/jni/android_location_GpsLocationProvider.cpp +++ b/core/jni/android_location_GpsLocationProvider.cpp @@ -270,6 +270,12 @@ static void android_location_GpsLocationProvider_inject_time(JNIEnv* env, jobjec sGpsInterface->inject_time(time, timeReference, uncertainty); } +static void android_location_GpsLocationProvider_inject_location(JNIEnv* env, jobject obj, + jdouble latitude, jdouble longitude, jfloat accuracy) +{ + sGpsInterface->inject_location(latitude, longitude, accuracy); +} + static jboolean android_location_GpsLocationProvider_supports_xtra(JNIEnv* env, jobject obj) { if (!sGpsXtraInterface) { @@ -353,6 +359,7 @@ static JNINativeMethod sMethods[] = { {"native_wait_for_event", "()V", (void*)android_location_GpsLocationProvider_wait_for_event}, {"native_read_sv_status", "([I[F[F[F[I)I", (void*)android_location_GpsLocationProvider_read_sv_status}, {"native_inject_time", "(JJI)V", (void*)android_location_GpsLocationProvider_inject_time}, + {"native_inject_location", "(DDF)V", (void*)android_location_GpsLocationProvider_inject_location}, {"native_supports_xtra", "()Z", (void*)android_location_GpsLocationProvider_supports_xtra}, {"native_inject_xtra_data", "([BI)V", (void*)android_location_GpsLocationProvider_inject_xtra_data}, {"native_agps_data_conn_open", "(Ljava/lang/String;)V", (void*)android_location_GpsLocationProvider_agps_data_conn_open}, diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index c13bf775c6fd..99fe9c8ecfe8 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -2849,6 +2849,14 @@ The default value is <code>false</code>. <i>Optional attribute.</i>. --> <attr name="includeInGlobalSearch" format="boolean" /> + <!-- If provided and <code>true</code>, this searchable activity will be invoked for all + queries in a particular session. If set to <code>false</code> and the activity + returned zero results for a query, it will not be invoked again in that session for + supersets of that zero-results query. For example, if the activity returned zero + results for "bo", it would not be queried again for "bob". + The default value is <code>false</code>. <i>Optional attribute.</i>. --> + <attr name="queryAfterZeroResults" format="boolean" /> + </declare-styleable> <!-- In order to process special action keys during search, you must define them using diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index a50f3f9d5782..e5a58b15e4a4 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -1115,6 +1115,7 @@ <public type="attr" name="backupAgent" /> <public type="attr" name="allowBackup" /> <public type="attr" name="glEsVersion" /> + <public type="attr" name="queryAfterZeroResults" /> <public-padding type="attr" name="donut_resource_pad" end="0x0101029f" /> diff --git a/core/res/res/xml/power_profile_default.xml b/core/res/res/xml/power_profile.xml index ceecb1a7e790..859902e73d9e 100644 --- a/core/res/res/xml/power_profile_default.xml +++ b/core/res/res/xml/power_profile.xml @@ -19,19 +19,22 @@ <device name="Android"> <item name="none">0</item> - <item name="screen.on">30</item> - <item name="bluetooth.active">103</item> - <item name="bluetooth.on">5</item> - <item name="screen.full">114</item> - <item name="wifi.on">23</item> - <item name="wifi.active">200</item> - <item name="wifi.scan">200</item> - <item name="cpu.idle">1.6</item> - <item name="cpu.normal">100</item> - <item name="cpu.full">140</item> - <item name="dsp.audio">70</item> - <item name="dsp.video">100</item> - <item name="radio.on">3</item> - <item name="radio.active">175</item> - <item name="gps.on">120</item> + <item name="screen.on">0.1</item> + <item name="bluetooth.active">0.1</item> + <item name="bluetooth.on">0.1</item> + <item name="screen.full">0.1</item> + <item name="wifi.on">0.1</item> + <item name="wifi.active">0.1</item> + <item name="wifi.scan">0.1</item> + <item name="cpu.idle">0.1</item> + <item name="cpu.normal">0.2</item> + <item name="cpu.full">1</item> + <item name="dsp.audio">0.1</item> + <item name="dsp.video">0.1</item> + <item name="radio.active">1</item> + <item name="gps.on">1</item> + <array name="radio.on"> <!-- Strength 0 to BINS-1 --> + <value>1</value> + <value>0.1</value> + </array> </device> diff --git a/docs/html/search.jd b/docs/html/search.jd index 0a802a6bfcbe..defba3087af3 100644 --- a/docs/html/search.jd +++ b/docs/html/search.jd @@ -2,7 +2,7 @@ page.title=Search Results @jd:body
<script src="http://www.google.com/jsapi" type="text/javascript"></script>
-<script src="/assets/jquery-history.js" type="text/javascript"></script>
+<script src="{@docRoot}assets/jquery-history.js" type="text/javascript"></script>
<script type="text/javascript">
google.load('search', '1');
diff --git a/keystore/java/android/security/Keystore.java b/keystore/java/android/security/Keystore.java new file mode 100644 index 000000000000..3f83473199ae --- /dev/null +++ b/keystore/java/android/security/Keystore.java @@ -0,0 +1,103 @@ +/* + * 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.security; + +/** + * The Keystore class provides the functions to list the certs/keys in keystore. + * {@hide} + */ +public abstract class Keystore { + private static final String TAG = "Keystore"; + private static final String[] NOTFOUND = new String[0]; + + /** + */ + public static Keystore getInstance() { + return new FileKeystore(); + } + + /** + */ + public abstract String getUserkey(String key); + + /** + */ + public abstract String getCertificate(String key); + + /** + */ + public abstract String[] getAllCertificateKeys(); + + /** + */ + public abstract String[] getAllUserkeyKeys(); + + private static class FileKeystore extends Keystore { + private static final String SERVICE_NAME = "keystore"; + private static final String LIST_CERTIFICATES = "listcerts"; + private static final String LIST_USERKEYS = "listuserkeys"; + private static final String PATH = "/data/misc/keystore/"; + private static final String USERKEY_PATH = PATH + "userkeys/"; + private static final String CERT_PATH = PATH + "certs/"; + private static final ServiceCommand mServiceCommand = + new ServiceCommand(SERVICE_NAME); + + @Override + public String getUserkey(String key) { + return USERKEY_PATH + key; + } + + @Override + public String getCertificate(String key) { + return CERT_PATH + key; + } + + /** + * Returns the array of the certificate names in keystore if successful. + * Or return an empty array if error. + * + * @return array of the certificates + */ + @Override + public String[] getAllCertificateKeys() { + try { + String result = mServiceCommand.execute(LIST_CERTIFICATES); + if (result != null) return result.split("\\s+"); + return NOTFOUND; + } catch (NumberFormatException ex) { + return NOTFOUND; + } + } + + /** + * Returns the array of the names of private keys in keystore if successful. + * Or return an empty array if errors. + * + * @return array of the user keys + */ + @Override + public String[] getAllUserkeyKeys() { + try { + String result = mServiceCommand.execute(LIST_USERKEYS); + if (result != null) return result.split("\\s+"); + return NOTFOUND; + } catch (NumberFormatException ex) { + return NOTFOUND; + } + } + } +} diff --git a/keystore/java/android/security/ServiceCommand.java b/keystore/java/android/security/ServiceCommand.java new file mode 100644 index 000000000000..f1d4302e70c8 --- /dev/null +++ b/keystore/java/android/security/ServiceCommand.java @@ -0,0 +1,178 @@ +/* + * 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.security; + +import android.net.LocalSocketAddress; +import android.net.LocalSocket; +import android.util.Config; +import android.util.Log; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.Socket; + +/* + * ServiceCommand is used to connect to a service throught the local socket, + * and send out the command, return the result to the caller. + * {@hide} + */ +public class ServiceCommand { + public static final String SUCCESS = "0"; + public static final String FAILED = "-1"; + + private String mServiceName; + private String mTag; + private InputStream mIn; + private OutputStream mOut; + private LocalSocket mSocket; + private static final int BUFFER_LENGTH = 1024; + + private byte buf[] = new byte[BUFFER_LENGTH]; + private int buflen = 0; + + private boolean connect() { + if (mSocket != null) { + return true; + } + Log.i(mTag, "connecting..."); + try { + mSocket = new LocalSocket(); + + LocalSocketAddress address = new LocalSocketAddress( + mServiceName, LocalSocketAddress.Namespace.RESERVED); + + mSocket.connect(address); + + mIn = mSocket.getInputStream(); + mOut = mSocket.getOutputStream(); + } catch (IOException ex) { + disconnect(); + return false; + } + return true; + } + + private void disconnect() { + Log.i(mTag,"disconnecting..."); + try { + if (mSocket != null) mSocket.close(); + } catch (IOException ex) { } + try { + if (mIn != null) mIn.close(); + } catch (IOException ex) { } + try { + if (mOut != null) mOut.close(); + } catch (IOException ex) { } + mSocket = null; + mIn = null; + mOut = null; + } + + private boolean readBytes(byte buffer[], int len) { + int off = 0, count; + if (len < 0) return false; + while (off != len) { + try { + count = mIn.read(buffer, off, len - off); + if (count <= 0) { + Log.e(mTag, "read error " + count); + break; + } + off += count; + } catch (IOException ex) { + Log.e(mTag,"read exception"); + break; + } + } + if (off == len) return true; + disconnect(); + return false; + } + + private boolean readReply() { + int len, ret; + buflen = 0; + + if (!readBytes(buf, 2)) return false; + ret = (((int) buf[0]) & 0xff) | ((((int) buf[1]) & 0xff) << 8); + if (ret != 0) return false; + + if (!readBytes(buf, 2)) return false; + len = (((int) buf[0]) & 0xff) | ((((int) buf[1]) & 0xff) << 8); + if (len > BUFFER_LENGTH) { + Log.e(mTag,"invalid reply length (" + len + ")"); + disconnect(); + return false; + } + if (!readBytes(buf, len)) return false; + buflen = len; + return true; + } + + private boolean writeCommand(String _cmd) { + byte[] cmd = _cmd.getBytes(); + int len = cmd.length; + if ((len < 1) || (len > BUFFER_LENGTH)) return false; + buf[0] = (byte) (len & 0xff); + buf[1] = (byte) ((len >> 8) & 0xff); + try { + mOut.write(buf, 0, 2); + mOut.write(cmd, 0, len); + } catch (IOException ex) { + Log.e(mTag,"write error"); + disconnect(); + return false; + } + return true; + } + + private String executeCommand(String cmd) { + if (!writeCommand(cmd)) { + /* If service died and restarted in the background + * (unlikely but possible) we'll fail on the next + * write (this one). Try to reconnect and write + * the command one more time before giving up. + */ + Log.e(mTag, "write command failed? reconnect!"); + if (!connect() || !writeCommand(cmd)) { + return null; + } + } + if (readReply()) { + return new String(buf, 0, buflen); + } else { + return null; + } + } + + public synchronized String execute(String cmd) { + String result; + if (!connect()) { + Log.e(mTag, "connection failed"); + return null; + } + result = executeCommand(cmd); + disconnect(); + return result; + } + + public ServiceCommand(String service) { + mServiceName = service; + mTag = service; + } +} diff --git a/location/java/com/android/internal/location/GpsLocationProvider.java b/location/java/com/android/internal/location/GpsLocationProvider.java index 725fbf9ce85b..969855323248 100644 --- a/location/java/com/android/internal/location/GpsLocationProvider.java +++ b/location/java/com/android/internal/location/GpsLocationProvider.java @@ -410,6 +410,10 @@ public class GpsLocationProvider extends ILocationProvider.Stub { * Someday we might use this for network location injection to aid the GPS */ public void updateLocation(Location location) { + if (location.hasAccuracy()) { + native_inject_location(location.getLatitude(), location.getLongitude(), + location.getAccuracy()); + } } /** @@ -1210,7 +1214,8 @@ public class GpsLocationProvider extends ILocationProvider.Stub { // mask[0] is ephemeris mask and mask[1] is almanac mask private native int native_read_sv_status(int[] svs, float[] snrs, float[] elevations, float[] azimuths, int[] masks); - + private native void native_inject_location(double latitude, double longitude, float accuracy); + // XTRA Support private native void native_inject_time(long time, long timeReference, int uncertainty); private native boolean native_supports_xtra(); 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 e01bd530cec8..ea42f53155df 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPlayerApiTest.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/functional/MediaPlayerApiTest.java @@ -80,7 +80,6 @@ public class MediaPlayerApiTest extends ActivityInstrumentationTestCase<MediaFra assertTrue("MIDI getDuration", duratoinWithinTolerence); } - @Suppress @MediumTest public void testWMA9GetDuration() throws Exception { int duration = CodecTest.getDuration(MediaNames.WMA9); @@ -122,7 +121,6 @@ public class MediaPlayerApiTest extends ActivityInstrumentationTestCase<MediaFra assertTrue("MIDI GetCurrentPosition", currentPosition); } - @Suppress @LargeTest public void testWMA9GetCurrentPosition() throws Exception { boolean currentPosition = CodecTest.getCurrentPosition(MediaNames.WMA9); @@ -160,7 +158,6 @@ public class MediaPlayerApiTest extends ActivityInstrumentationTestCase<MediaFra assertTrue("MIDI Pause", isPaused); } - @Suppress @LargeTest public void testWMA9Pause() throws Exception { boolean isPaused = CodecTest.pause(MediaNames.WMA9); @@ -232,7 +229,6 @@ public class MediaPlayerApiTest extends ActivityInstrumentationTestCase<MediaFra assertTrue("MIDI setLooping", isLoop); } - @Suppress @LargeTest public void testWMA9SetLooping() throws Exception { boolean isLoop = CodecTest.setLooping(MediaNames.WMA9); @@ -271,7 +267,6 @@ public class MediaPlayerApiTest extends ActivityInstrumentationTestCase<MediaFra assertTrue("MIDI seekTo", isLoop); } - @Suppress @LargeTest public void testWMA9SeekTo() throws Exception { boolean isLoop = CodecTest.seekTo(MediaNames.WMA9); @@ -310,7 +305,7 @@ public class MediaPlayerApiTest extends ActivityInstrumentationTestCase<MediaFra boolean isEnd = CodecTest.seekToEnd(MediaNames.MIDI); assertTrue("MIDI seekToEnd", isEnd); } - + @Suppress @LargeTest public void testWMA9SeekToEnd() throws Exception { @@ -388,7 +383,6 @@ public class MediaPlayerApiTest extends ActivityInstrumentationTestCase<MediaFra assertTrue("H264AMR SeekTo", isSeek); } - @Suppress @LargeTest public void testVideoWMVSeekTo() throws Exception { boolean isSeek = CodecTest.videoSeekTo(MediaNames.VIDEO_WMV); diff --git a/packages/TtsService/src/android/tts/TtsService.java b/packages/TtsService/src/android/tts/TtsService.java index c2c76fd34060..8a64113fb151 100755 --- a/packages/TtsService/src/android/tts/TtsService.java +++ b/packages/TtsService/src/android/tts/TtsService.java @@ -117,16 +117,7 @@ public class TtsService extends Service implements OnCompletionListener { // app. prefs = PreferenceManager.getDefaultSharedPreferences(this); - PackageManager pm = this.getPackageManager(); - String soLibPath = ""; - try { - soLibPath = pm.getApplicationInfo("com.svox.pico", 0).dataDir; - } catch (NameNotFoundException e) { - // This exception cannot actually happen as com.svox.pico is - // included with the system image. - e.printStackTrace(); - } - soLibPath = soLibPath + "/lib/libttspico.so"; + String soLibPath = "/system/lib/libttspico.so"; nativeSynth = new SynthProxy(soLibPath); mSelf = this; diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java index 42a895c1ac50..d3067ec3a5c8 100644 --- a/services/java/com/android/server/BackupManagerService.java +++ b/services/java/com/android/server/BackupManagerService.java @@ -25,6 +25,7 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.ApplicationInfo; +import android.content.pm.IPackageDataObserver; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; @@ -46,13 +47,14 @@ import android.backup.IRestoreSession; import android.backup.BackupManager; import android.backup.RestoreSet; -import com.android.internal.backup.AdbTransport; +import com.android.internal.backup.LocalTransport; import com.android.internal.backup.GoogleTransport; import com.android.internal.backup.IBackupTransport; import java.io.File; import java.io.FileDescriptor; import java.io.FileNotFoundException; +import java.io.IOException; import java.io.PrintWriter; import java.lang.String; import java.util.ArrayList; @@ -70,7 +72,11 @@ class BackupManagerService extends IBackupManager.Stub { private static final int MSG_RUN_BACKUP = 1; private static final int MSG_RUN_FULL_BACKUP = 2; - + private static final int MSG_RUN_RESTORE = 3; + + // Timeout interval for deciding that a bind or clear-data has taken too long + static final long TIMEOUT_INTERVAL = 10 * 1000; + private Context mContext; private PackageManager mPackageManager; private final IActivityManager mActivityManager; @@ -108,6 +114,10 @@ class BackupManagerService extends IBackupManager.Stub { private IBackupAgent mConnectedAgent; private volatile boolean mConnecting; + // A similar synchronicity mechanism around clearing apps' data for restore + private final Object mClearDataLock = new Object(); + private volatile boolean mClearingData; + private int mTransportId; private File mStateDir; @@ -122,7 +132,9 @@ class BackupManagerService extends IBackupManager.Stub { mStateDir = new File(Environment.getDataDirectory(), "backup"); mStateDir.mkdirs(); mDataDir = Environment.getDownloadCacheDirectory(); - mTransportId = BackupManager.TRANSPORT_GOOGLE; + + //!!! TODO: default to cloud transport, not local + mTransportId = BackupManager.TRANSPORT_LOCAL; // Build our mapping of uid to backup client services synchronized (mBackupParticipants) { @@ -203,81 +215,15 @@ class BackupManagerService extends IBackupManager.Stub { case MSG_RUN_FULL_BACKUP: break; - } - } - } - - void processOneBackup(BackupRequest request, IBackupAgent agent, IBackupTransport transport) { - final String packageName = request.appInfo.packageName; - Log.d(TAG, "processOneBackup doBackup() on " + packageName); - - try { - // Look up the package info & signatures. This is first so that if it - // throws an exception, there's no file setup yet that would need to - // be unraveled. - PackageInfo packInfo = mPackageManager.getPackageInfo(packageName, - PackageManager.GET_SIGNATURES); - - // !!! TODO: get the state file dir from the transport - File savedStateName = new File(mStateDir, packageName); - File backupDataName = new File(mDataDir, packageName + ".data"); - File newStateName = new File(mStateDir, packageName + ".new"); - - // In a full backup, we pass a null ParcelFileDescriptor as - // the saved-state "file" - ParcelFileDescriptor savedState = (request.fullBackup) ? null - : ParcelFileDescriptor.open(savedStateName, - ParcelFileDescriptor.MODE_READ_ONLY | - ParcelFileDescriptor.MODE_CREATE); - - backupDataName.delete(); - ParcelFileDescriptor backupData = - ParcelFileDescriptor.open(backupDataName, - ParcelFileDescriptor.MODE_READ_WRITE | - ParcelFileDescriptor.MODE_CREATE); - - newStateName.delete(); - ParcelFileDescriptor newState = - ParcelFileDescriptor.open(newStateName, - ParcelFileDescriptor.MODE_READ_WRITE | - ParcelFileDescriptor.MODE_CREATE); - // Run the target's backup pass - boolean success = false; - try { - agent.doBackup(savedState, backupData, newState); - success = true; - } finally { - if (savedState != null) { - savedState.close(); - } - backupData.close(); - newState.close(); - } - - // Now propagate the newly-backed-up data to the transport - if (success) { - if (DEBUG) Log.v(TAG, "doBackup() success; calling transport"); - backupData = - ParcelFileDescriptor.open(backupDataName, ParcelFileDescriptor.MODE_READ_ONLY); - int error = transport.performBackup(packInfo, backupData); - - // !!! TODO: After successful transport, delete the now-stale data - // and juggle the files so that next time the new state is passed - //backupDataName.delete(); - newStateName.renameTo(savedStateName); - } - } catch (NameNotFoundException e) { - Log.e(TAG, "Package not found on backup: " + packageName); - } catch (FileNotFoundException fnf) { - Log.w(TAG, "File not found on backup: "); - fnf.printStackTrace(); - } catch (RemoteException e) { - Log.d(TAG, "Remote target " + request.appInfo.packageName + " threw during backup:"); - e.printStackTrace(); - } catch (Exception e) { - Log.w(TAG, "Final exception guard in backup: "); - e.printStackTrace(); + case MSG_RUN_RESTORE: + { + int token = msg.arg1; + IBackupTransport transport = (IBackupTransport)msg.obj; + (new PerformRestoreThread(transport, token)).run(); + break; + } + } } } @@ -396,9 +342,9 @@ class BackupManagerService extends IBackupManager.Stub { private IBackupTransport createTransport(int transportID) { IBackupTransport transport = null; switch (transportID) { - case BackupManager.TRANSPORT_ADB: - if (DEBUG) Log.v(TAG, "Initializing adb transport"); - transport = new AdbTransport(); + case BackupManager.TRANSPORT_LOCAL: + if (DEBUG) Log.v(TAG, "Initializing local transport"); + transport = new LocalTransport(mContext); break; case BackupManager.TRANSPORT_GOOGLE: @@ -424,11 +370,14 @@ class BackupManagerService extends IBackupManager.Stub { Log.d(TAG, "awaiting agent for " + app); // success; wait for the agent to arrive - while (mConnecting && mConnectedAgent == null) { + // only wait 10 seconds for the clear data to happen + long timeoutMark = System.currentTimeMillis() + TIMEOUT_INTERVAL; + while (mConnecting && mConnectedAgent == null + && (System.currentTimeMillis() < timeoutMark)) { try { - mAgentConnectLock.wait(10000); + mAgentConnectLock.wait(5000); } catch (InterruptedException e) { - // just retry + // just bail return null; } } @@ -447,6 +396,37 @@ class BackupManagerService extends IBackupManager.Stub { return agent; } + // clear an application's data, blocking until the operation completes or times out + void clearApplicationDataSynchronous(String packageName) { + ClearDataObserver observer = new ClearDataObserver(); + + synchronized(mClearDataLock) { + mClearingData = true; + mPackageManager.clearApplicationUserData(packageName, observer); + + // only wait 10 seconds for the clear data to happen + long timeoutMark = System.currentTimeMillis() + TIMEOUT_INTERVAL; + while (mClearingData && (System.currentTimeMillis() < timeoutMark)) { + try { + mClearDataLock.wait(5000); + } catch (InterruptedException e) { + // won't happen, but still. + mClearingData = false; + } + } + } + } + + class ClearDataObserver extends IPackageDataObserver.Stub { + public void onRemoveCompleted(String packageName, boolean succeeded) + throws android.os.RemoteException { + synchronized(mClearDataLock) { + mClearingData = false; + notifyAll(); + } + } + } + // ----- Back up a set of applications via a worker thread ----- class PerformBackupThread extends Thread { @@ -508,13 +488,88 @@ class BackupManagerService extends IBackupManager.Stub { mActivityManager.unbindBackupAgent(request.appInfo); } catch (SecurityException ex) { // Try for the next one. - Log.d(TAG, "error in bind", ex); + Log.d(TAG, "error in bind/backup", ex); } catch (RemoteException e) { - // can't happen + Log.v(TAG, "bind/backup threw"); + e.printStackTrace(); } } } + + void processOneBackup(BackupRequest request, IBackupAgent agent, IBackupTransport transport) { + final String packageName = request.appInfo.packageName; + Log.d(TAG, "processOneBackup doBackup() on " + packageName); + + try { + // Look up the package info & signatures. This is first so that if it + // throws an exception, there's no file setup yet that would need to + // be unraveled. + PackageInfo packInfo = mPackageManager.getPackageInfo(packageName, + PackageManager.GET_SIGNATURES); + + // !!! TODO: get the state file dir from the transport + File savedStateName = new File(mStateDir, packageName); + File backupDataName = new File(mDataDir, packageName + ".data"); + File newStateName = new File(mStateDir, packageName + ".new"); + + // In a full backup, we pass a null ParcelFileDescriptor as + // the saved-state "file" + ParcelFileDescriptor savedState = (request.fullBackup) ? null + : ParcelFileDescriptor.open(savedStateName, + ParcelFileDescriptor.MODE_READ_ONLY | + ParcelFileDescriptor.MODE_CREATE); + + backupDataName.delete(); + ParcelFileDescriptor backupData = + ParcelFileDescriptor.open(backupDataName, + ParcelFileDescriptor.MODE_READ_WRITE | + ParcelFileDescriptor.MODE_CREATE); + + newStateName.delete(); + ParcelFileDescriptor newState = + ParcelFileDescriptor.open(newStateName, + ParcelFileDescriptor.MODE_READ_WRITE | + ParcelFileDescriptor.MODE_CREATE); + + // Run the target's backup pass + boolean success = false; + try { + agent.doBackup(savedState, backupData, newState); + success = true; + } finally { + if (savedState != null) { + savedState.close(); + } + backupData.close(); + newState.close(); + } + + // Now propagate the newly-backed-up data to the transport + if (success) { + if (DEBUG) Log.v(TAG, "doBackup() success; calling transport"); + backupData = + ParcelFileDescriptor.open(backupDataName, ParcelFileDescriptor.MODE_READ_ONLY); + int error = transport.performBackup(packInfo, backupData); + + // !!! TODO: After successful transport, delete the now-stale data + // and juggle the files so that next time the new state is passed + //backupDataName.delete(); + newStateName.renameTo(savedStateName); + } + } catch (NameNotFoundException e) { + Log.e(TAG, "Package not found on backup: " + packageName); + } catch (FileNotFoundException fnf) { + Log.w(TAG, "File not found on backup: "); + fnf.printStackTrace(); + } catch (RemoteException e) { + Log.d(TAG, "Remote target " + request.appInfo.packageName + " threw during backup:"); + e.printStackTrace(); + } catch (Exception e) { + Log.w(TAG, "Final exception guard in backup: "); + e.printStackTrace(); + } + } } @@ -524,12 +579,12 @@ class BackupManagerService extends IBackupManager.Stub { // ApplicationInfo struct if it is; null if not. // // !!! TODO: also consider signatures - ApplicationInfo isRestorable(PackageInfo packageInfo) { + PackageInfo isRestorable(PackageInfo packageInfo) { if (packageInfo.packageName != null) { try { - ApplicationInfo app = mPackageManager.getApplicationInfo(packageInfo.packageName, + PackageInfo app = mPackageManager.getPackageInfo(packageInfo.packageName, PackageManager.GET_SIGNATURES); - if ((app.flags & ApplicationInfo.FLAG_ALLOW_BACKUP) != 0) { + if ((app.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_BACKUP) != 0) { return app; } } catch (Exception e) { @@ -541,9 +596,12 @@ class BackupManagerService extends IBackupManager.Stub { class PerformRestoreThread extends Thread { private IBackupTransport mTransport; + private int mToken; + private RestoreSet mImage; - PerformRestoreThread(IBackupTransport transport) { + PerformRestoreThread(IBackupTransport transport, int restoreSetToken) { mTransport = transport; + mToken = restoreSetToken; } @Override @@ -556,6 +614,7 @@ class BackupManagerService extends IBackupManager.Stub { * 3. for each app in the restore set: * 3.a. if it's restorable on this device, add it to the restore queue * 4. for each app in the restore queue: + * 4.a. clear the app data * 4.b. get the restore data for the app from the transport * 4.c. launch the backup agent for the app * 4.d. agent.doRestore() with the data from the server @@ -576,14 +635,15 @@ class BackupManagerService extends IBackupManager.Stub { try { RestoreSet[] images = mTransport.getAvailableRestoreSets(); if (images.length > 0) { - // !!! for now we always take the first set - RestoreSet image = images[0]; + // !!! TODO: pick out the set for this token + mImage = images[0]; // build the set of apps we will attempt to restore - PackageInfo[] packages = mTransport.getAppSet(image.token); - HashSet<ApplicationInfo> appsToRestore = new HashSet<ApplicationInfo>(); + PackageInfo[] packages = mTransport.getAppSet(mImage.token); + HashSet<PackageInfo> appsToRestore = new HashSet<PackageInfo>(); for (PackageInfo pkg: packages) { - ApplicationInfo app = isRestorable(pkg); + // get the real PackageManager idea of the package + PackageInfo app = isRestorable(pkg); if (app != null) { appsToRestore.add(app); } @@ -609,19 +669,23 @@ class BackupManagerService extends IBackupManager.Stub { } // restore each app in the queue - void doQueuedRestores(HashSet<ApplicationInfo> appsToRestore) { - for (ApplicationInfo app : appsToRestore) { + void doQueuedRestores(HashSet<PackageInfo> appsToRestore) { + for (PackageInfo app : appsToRestore) { Log.d(TAG, "starting agent for restore of " + app); - IBackupAgent agent = null; try { - agent = bindToAgentSynchronous(app, IApplicationThread.BACKUP_MODE_RESTORE); + // Remove the app's data first + clearApplicationDataSynchronous(app.packageName); + + // Now perform the restore into the clean app + IBackupAgent agent = bindToAgentSynchronous(app.applicationInfo, + IApplicationThread.BACKUP_MODE_RESTORE); if (agent != null) { processOneRestore(app, agent); } // unbind even on timeout, just in case - mActivityManager.unbindBackupAgent(app); + mActivityManager.unbindBackupAgent(app.applicationInfo); } catch (SecurityException ex) { // Try for the next one. Log.d(TAG, "error in bind", ex); @@ -632,9 +696,67 @@ class BackupManagerService extends IBackupManager.Stub { } } - // do the guts of a restore - void processOneRestore(ApplicationInfo app, IBackupAgent agent) { + // Do the guts of a restore of one application, derived from the 'mImage' + // restore set via the 'mTransport' transport. + void processOneRestore(PackageInfo app, IBackupAgent agent) { // !!! TODO: actually run the restore through mTransport + final String packageName = app.packageName; + + // !!! TODO: get the dirs from the transport + File backupDataName = new File(mDataDir, packageName + ".restore"); + backupDataName.delete(); + try { + ParcelFileDescriptor backupData = + ParcelFileDescriptor.open(backupDataName, + ParcelFileDescriptor.MODE_READ_WRITE | + ParcelFileDescriptor.MODE_CREATE); + + // Run the transport's restore pass + // Run the target's backup pass + int err = -1; + try { + err = mTransport.getRestoreData(mImage.token, app, backupData); + } catch (RemoteException e) { + // can't happen + } finally { + backupData.close(); + } + + // Okay, we have the data. Now have the agent do the restore. + File newStateName = new File(mStateDir, packageName + ".new"); + ParcelFileDescriptor newState = + ParcelFileDescriptor.open(newStateName, + ParcelFileDescriptor.MODE_READ_WRITE | + ParcelFileDescriptor.MODE_CREATE); + + backupData = ParcelFileDescriptor.open(backupDataName, + ParcelFileDescriptor.MODE_READ_ONLY); + + boolean success = false; + try { + agent.doRestore(backupData, newState); + success = true; + } catch (Exception e) { + Log.e(TAG, "Restore failed for " + packageName); + e.printStackTrace(); + } finally { + newState.close(); + backupData.close(); + } + + // if everything went okay, remember the recorded state now + if (success) { + File savedStateName = new File(mStateDir, packageName); + newStateName.renameTo(savedStateName); + } + } catch (FileNotFoundException fnfe) { + Log.v(TAG, "Couldn't open file for restore: " + fnfe); + } catch (IOException ioe) { + Log.e(TAG, "Unable to process restore file: " + ioe); + } catch (Exception e) { + Log.e(TAG, "Final exception guard in restore:"); + e.printStackTrace(); + } } } @@ -761,6 +883,9 @@ class BackupManagerService extends IBackupManager.Stub { // --- Binder interface --- public RestoreSet[] getAvailableRestoreSets() throws android.os.RemoteException { + mContext.enforceCallingPermission("android.permission.BACKUP", + "getAvailableRestoreSets"); + synchronized(this) { if (mRestoreSets == null) { mRestoreSets = mRestoreTransport.getAvailableRestoreSets(); @@ -770,10 +895,26 @@ class BackupManagerService extends IBackupManager.Stub { } public int performRestore(int token) throws android.os.RemoteException { + mContext.enforceCallingPermission("android.permission.BACKUP", "performRestore"); + + if (mRestoreSets != null) { + for (int i = 0; i < mRestoreSets.length; i++) { + if (token == mRestoreSets[i].token) { + Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE, + mRestoreTransport); + msg.arg1 = token; + mBackupHandler.sendMessage(msg); + return 0; + } + } + } return -1; } public void endRestoreSession() throws android.os.RemoteException { + mContext.enforceCallingPermission("android.permission.BACKUP", + "endRestoreSession"); + mRestoreTransport.endSession(); mRestoreTransport = null; } diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java index adf07dd59293..fc3729050c9e 100644 --- a/services/java/com/android/server/LocationManagerService.java +++ b/services/java/com/android/server/LocationManagerService.java @@ -769,8 +769,8 @@ public class LocationManagerService extends ILocationManager.Stub implements Run if (!record.mReceiver.callProviderEnabledLocked(provider, enabled)) { if (deadReceivers == null) { deadReceivers = new ArrayList<Receiver>(); - deadReceivers.add(record.mReceiver); } + deadReceivers.add(record.mReceiver); } listeners++; } diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index 14dcfd5ab97c..6a81178ee0d8 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -10422,11 +10422,14 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // Not backing this app up any more; reset its OOM adjustment updateOomAdjLocked(proc); - try { - proc.thread.scheduleDestroyBackupAgent(appInfo); - } catch (Exception e) { - Log.e(TAG, "Exception when unbinding backup agent:"); - e.printStackTrace(); + // If the app crashed during backup, 'thread' will be null here + if (proc.thread != null) { + try { + proc.thread.scheduleDestroyBackupAgent(appInfo); + } catch (Exception e) { + Log.e(TAG, "Exception when unbinding backup agent:"); + e.printStackTrace(); + } } } } |