Merge change 3828 into donut
* changes:
Renaming the Tts class to TextToSpeech for clarity. Switching to HashMap for parameter lists to pass key-value parameter pairs. Using TODO instead of FIXME in comments. Removing methods that don't belong anymore to the TTS interface now the feature becomes part of the platform.
diff --git a/api/current.xml b/api/current.xml
index cb348e1..f30c505 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 7474d81..e53cece 100644
--- a/cmds/keystore/commands.c
+++ b/cmds/keystore/commands.c
@@ -40,7 +40,7 @@
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 dbb62b37..5193b3d 100644
--- a/cmds/keystore/keystore.c
+++ b/cmds/keystore/keystore.c
@@ -113,7 +113,7 @@
unsigned i;
unsigned n = 0;
unsigned short count;
- int ret = -1;
+ short ret = -1;
/* default reply is "" */
reply[0] = 0;
@@ -139,7 +139,7 @@
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 @@
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 (n > BUFFER_MAX) n = BUFFER_MAX;
- count = n;
-
- if (writex(s, &count, sizeof(count))) return -1;
- if (writex(s, cmd, count)) return -1;
+ 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;
+ }
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 @@
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 3f0aa95..820f192 100644
--- a/core/java/android/app/SearchManager.java
+++ b/core/java/android/app/SearchManager.java
@@ -1402,6 +1402,16 @@
* @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 6e8bd78..8c5cee3 100644
--- a/core/java/android/app/SuggestionsAdapter.java
+++ b/core/java/android/app/SuggestionsAdapter.java
@@ -20,16 +20,19 @@
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 @@
*/
@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 @@
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 @@
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 c3b6a02..8df7eae 100644
--- a/core/java/android/backup/BackupManager.java
+++ b/core/java/android/backup/BackupManager.java
@@ -45,7 +45,7 @@
/**
* 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 c083142..4df7368 100644
--- a/core/java/android/server/search/SearchableInfo.java
+++ b/core/java/android/server/search/SearchableInfo.java
@@ -66,6 +66,7 @@
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 @@
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 @@
}
/**
+ * 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 @@
mSearchInputType = in.readInt();
mSearchImeOptions = in.readInt();
mIncludeInGlobalSearch = in.readInt() != 0;
+ mQueryAfterZeroResults = in.readInt() != 0;
mSuggestAuthority = in.readString();
mSuggestPath = in.readString();
@@ -702,6 +717,7 @@
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 f376ce5..585ce3d 100644
--- a/core/java/android/widget/AutoCompleteTextView.java
+++ b/core/java/android/widget/AutoCompleteTextView.java
@@ -123,10 +123,6 @@
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 @@
/** {@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 @@
@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 8d3bd1c..0000000
--- 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 0000000..62fba4a
--- /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 99a381c..16a3bad 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.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 @@
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 @@
boolean mScreenOn;
StopwatchTimer mScreenOnTimer;
-
+
int mScreenBrightnessBin = -1;
final StopwatchTimer[] mScreenBrightnessTimer = new StopwatchTimer[NUM_SCREEN_BRIGHTNESS_BINS];
@@ -137,10 +139,10 @@
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 @@
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 @@
}
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 @@
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 @@
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 void noteStopGps(int uid) {
mUidStats.get(uid).noteStopGps();
}
-
+
public void noteScreenOnLocked() {
if (!mScreenOn) {
mScreenOn = true;
@@ -1039,6 +1098,7 @@
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 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 @@
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 @@
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 f08dddd..a37bf6e 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.XmlPullParserException;
import java.io.IOException;
+import java.util.ArrayList;
import java.util.HashMap;
/**
@@ -118,26 +119,28 @@
*/
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 @@
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 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 0858741..5c4fb22 100644
--- a/core/jni/android_location_GpsLocationProvider.cpp
+++ b/core/jni/android_location_GpsLocationProvider.cpp
@@ -270,6 +270,12 @@
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 @@
{"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 c13bf775..99fe9c8 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 a50f3f9..e5a58b1 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.xml b/core/res/res/xml/power_profile.xml
new file mode 100644
index 0000000..859902e
--- /dev/null
+++ b/core/res/res/xml/power_profile.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 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.
+*/
+-->
+
+<device name="Android">
+ <item name="none">0</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/core/res/res/xml/power_profile_default.xml b/core/res/res/xml/power_profile_default.xml
deleted file mode 100644
index ceecb1a..0000000
--- a/core/res/res/xml/power_profile_default.xml
+++ /dev/null
@@ -1,37 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-**
-** Copyright 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.
-*/
--->
-
-<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>
-</device>
diff --git a/docs/html/search.jd b/docs/html/search.jd
index 0a802a6..defba30 100644
--- a/docs/html/search.jd
+++ b/docs/html/search.jd
@@ -2,7 +2,7 @@
@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 0000000..3f83473
--- /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 0000000..f1d4302
--- /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 725fbf9..9698553 100644
--- a/location/java/com/android/internal/location/GpsLocationProvider.java
+++ b/location/java/com/android/internal/location/GpsLocationProvider.java
@@ -410,6 +410,10 @@
* 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 @@
// 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 e01bd53..ea42f53 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 @@
assertTrue("MIDI getDuration", duratoinWithinTolerence);
}
- @Suppress
@MediumTest
public void testWMA9GetDuration() throws Exception {
int duration = CodecTest.getDuration(MediaNames.WMA9);
@@ -122,7 +121,6 @@
assertTrue("MIDI GetCurrentPosition", currentPosition);
}
- @Suppress
@LargeTest
public void testWMA9GetCurrentPosition() throws Exception {
boolean currentPosition = CodecTest.getCurrentPosition(MediaNames.WMA9);
@@ -160,7 +158,6 @@
assertTrue("MIDI Pause", isPaused);
}
- @Suppress
@LargeTest
public void testWMA9Pause() throws Exception {
boolean isPaused = CodecTest.pause(MediaNames.WMA9);
@@ -232,7 +229,6 @@
assertTrue("MIDI setLooping", isLoop);
}
- @Suppress
@LargeTest
public void testWMA9SetLooping() throws Exception {
boolean isLoop = CodecTest.setLooping(MediaNames.WMA9);
@@ -271,7 +267,6 @@
assertTrue("MIDI seekTo", isLoop);
}
- @Suppress
@LargeTest
public void testWMA9SeekTo() throws Exception {
boolean isLoop = CodecTest.seekTo(MediaNames.WMA9);
@@ -310,7 +305,7 @@
boolean isEnd = CodecTest.seekToEnd(MediaNames.MIDI);
assertTrue("MIDI seekToEnd", isEnd);
}
-
+
@Suppress
@LargeTest
public void testWMA9SeekToEnd() throws Exception {
@@ -388,7 +383,6 @@
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 c2c76fd..8a64113 100755
--- a/packages/TtsService/src/android/tts/TtsService.java
+++ b/packages/TtsService/src/android/tts/TtsService.java
@@ -117,16 +117,7 @@
// 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 42a895c..d3067ec 100644
--- a/services/java/com/android/server/BackupManagerService.java
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -25,6 +25,7 @@
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.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 @@
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 @@
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 @@
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 @@
case MSG_RUN_FULL_BACKUP:
break;
+
+ case MSG_RUN_RESTORE:
+ {
+ int token = msg.arg1;
+ IBackupTransport transport = (IBackupTransport)msg.obj;
+ (new PerformRestoreThread(transport, token)).run();
+ 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();
}
}
@@ -396,9 +342,9 @@
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 @@
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 @@
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 @@
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 @@
// 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 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 @@
* 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 @@
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 @@
}
// 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 @@
}
}
- // 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 @@
// --- 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 @@
}
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 adf07dd..fc37290 100644
--- a/services/java/com/android/server/LocationManagerService.java
+++ b/services/java/com/android/server/LocationManagerService.java
@@ -769,8 +769,8 @@
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 14dcfd5..6a81178 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -10422,11 +10422,14 @@
// 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();
+ }
}
}
}