Merge "a new java implementation of base64 for android-common"
diff --git a/api/current.xml b/api/current.xml
index 5e34322..0893e4e 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -24873,17 +24873,6 @@
visibility="public"
>
</field>
-<field name="INTENT_ACTION_SELECT_SEARCH_SOURCE"
- type="java.lang.String"
- transient="false"
- volatile="false"
- value=""android.intent.action.SELECT_SEARCH_SOURCE""
- static="true"
- final="true"
- deprecated="not deprecated"
- visibility="public"
->
-</field>
<field name="INTENT_ACTION_WEB_SEARCH_SETTINGS"
type="java.lang.String"
transient="false"
@@ -55617,7 +55606,7 @@
>
</field>
</class>
-<class name="GestureUtilities"
+<class name="GestureUtils"
extends="java.lang.Object"
abstract="false"
static="false"
@@ -80725,6 +80714,21 @@
<parameter name="tag" type="java.lang.String">
</parameter>
</method>
+<method name="getAttributeDouble"
+ return="double"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="tag" type="java.lang.String">
+</parameter>
+<parameter name="defaultValue" type="double">
+</parameter>
+</method>
<method name="getAttributeInt"
return="int"
abstract="false"
@@ -80924,6 +80928,17 @@
visibility="public"
>
</field>
+<field name="TAG_FOCAL_LENGTH"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value=""FocalLength""
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="TAG_GPS_DATESTAMP"
type="java.lang.String"
transient="false"
@@ -86759,7 +86774,7 @@
visibility="public"
>
</method>
-<method name="getMobileRxPkts"
+<method name="getMobileRxPackets"
return="long"
abstract="false"
native="false"
@@ -86781,7 +86796,7 @@
visibility="public"
>
</method>
-<method name="getMobileTxPkts"
+<method name="getMobileTxPackets"
return="long"
abstract="false"
native="false"
@@ -86803,7 +86818,7 @@
visibility="public"
>
</method>
-<method name="getTotalRxPkts"
+<method name="getTotalRxPackets"
return="long"
abstract="false"
native="false"
@@ -86825,7 +86840,7 @@
visibility="public"
>
</method>
-<method name="getTotalTxPkts"
+<method name="getTotalTxPackets"
return="long"
abstract="false"
native="false"
@@ -159338,6 +159353,21 @@
</parameter>
</method>
<method name="tokenize"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="text" type="java.lang.CharSequence">
+</parameter>
+<parameter name="out" type="java.util.Collection<android.text.util.Rfc822Token>">
+</parameter>
+</method>
+<method name="tokenize"
return="android.text.util.Rfc822Token[]"
abstract="false"
native="false"
@@ -186327,6 +186357,108 @@
</parameter>
</method>
</class>
+<class name="ConsoleMessage"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="ConsoleMessage"
+ type="android.webkit.ConsoleMessage"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="message" type="java.lang.String">
+</parameter>
+<parameter name="sourceId" type="java.lang.String">
+</parameter>
+<parameter name="lineNumber" type="int">
+</parameter>
+<parameter name="msgLevel" type="android.webkit.ConsoleMessage.MessageLevel">
+</parameter>
+</constructor>
+<method name="lineNumber"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="message"
+ return="java.lang.String"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="messageLevel"
+ return="android.webkit.ConsoleMessage.MessageLevel"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="sourceId"
+ return="java.lang.String"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+</class>
+<class name="ConsoleMessage.MessageLevel"
+ extends="java.lang.Enum"
+ abstract="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="valueOf"
+ return="android.webkit.ConsoleMessage.MessageLevel"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="name" type="java.lang.String">
+</parameter>
+</method>
+<method name="values"
+ return="android.webkit.ConsoleMessage.MessageLevel[]"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+</class>
<class name="CookieManager"
extends="java.lang.Object"
abstract="false"
@@ -187815,7 +187947,7 @@
synchronized="false"
static="false"
final="false"
- deprecated="not deprecated"
+ deprecated="deprecated"
visibility="public"
>
<parameter name="message" type="java.lang.String">
@@ -187825,6 +187957,19 @@
<parameter name="sourceID" type="java.lang.String">
</parameter>
</method>
+<method name="onConsoleMessage"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="consoleMessage" type="android.webkit.ConsoleMessage">
+</parameter>
+</method>
<method name="onCreateWindow"
return="boolean"
abstract="false"
diff --git a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
index 8c15d0b..acfbb07 100644
--- a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
+++ b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
@@ -315,7 +315,7 @@
for (RestoreSet s : sets) {
if (s.token == token) {
System.out.println("Scheduling restore: " + s.name);
- didRestore = (mRestore.performRestore(token, observer) == 0);
+ didRestore = (mRestore.restoreAll(token, observer) == 0);
break;
}
}
diff --git a/cmds/installd/commands.c b/cmds/installd/commands.c
index 79bda74..1e8555b 100644
--- a/cmds/installd/commands.c
+++ b/cmds/installd/commands.c
@@ -78,6 +78,30 @@
return delete_dir_contents(pkgdir, 1, 0);
}
+int renamepkg(const char *oldpkgname, const char *newpkgname, int encrypted_fs_flag)
+{
+ char oldpkgdir[PKG_PATH_MAX];
+ char newpkgdir[PKG_PATH_MAX];
+
+ if (encrypted_fs_flag == USE_UNENCRYPTED_FS) {
+ if (create_pkg_path(oldpkgdir, PKG_DIR_PREFIX, oldpkgname, PKG_DIR_POSTFIX))
+ return -1;
+ if (create_pkg_path(newpkgdir, PKG_DIR_PREFIX, newpkgname, PKG_DIR_POSTFIX))
+ return -1;
+ } else {
+ if (create_pkg_path(oldpkgdir, PKG_SEC_DIR_PREFIX, oldpkgname, PKG_DIR_POSTFIX))
+ return -1;
+ if (create_pkg_path(newpkgdir, PKG_SEC_DIR_PREFIX, newpkgname, PKG_DIR_POSTFIX))
+ return -1;
+ }
+
+ if (rename(oldpkgdir, newpkgdir) < 0) {
+ LOGE("cannot rename dir '%s' to '%s': %s\n", oldpkgdir, newpkgdir, strerror(errno));
+ return -errno;
+ }
+ return 0;
+}
+
int delete_user_data(const char *pkgname, int encrypted_fs_flag)
{
char pkgdir[PKG_PATH_MAX];
@@ -636,3 +660,203 @@
}
return -1;
}
+
+int create_move_path(char path[PKG_PATH_MAX],
+ const char* prefix,
+ const char* pkgname,
+ const char* leaf)
+{
+ if ((strlen(prefix) + strlen(pkgname) + strlen(leaf) + 1) >= PKG_PATH_MAX) {
+ return -1;
+ }
+
+ sprintf(path, "%s%s/%s", prefix, pkgname, leaf);
+ return 0;
+}
+
+void mkinnerdirs(char* path, int basepos, mode_t mode, int uid, int gid)
+{
+ while (path[basepos] != 0) {
+ if (path[basepos] == '/') {
+ path[basepos] = 0;
+ LOGI("Making directory: %s\n", path);
+ if (mkdir(path, mode) == 0) {
+ chown(path, uid, gid);
+ }
+ path[basepos] = '/';
+ basepos++;
+ }
+ basepos++;
+ }
+}
+
+int movefiles()
+{
+ DIR *d;
+ int dfd, subfd;
+ struct dirent *de;
+ struct stat s;
+ char buf[PKG_PATH_MAX+1];
+ int bufp, bufe, bufi, readlen;
+
+ char srcpkg[PKG_NAME_MAX];
+ char dstpkg[PKG_NAME_MAX];
+ char srcpath[PKG_PATH_MAX];
+ char dstpath[PKG_PATH_MAX];
+ int dstuid, dstgid;
+ int hasspace;
+
+ d = opendir(UPDATE_COMMANDS_DIR_PREFIX);
+ if (d == NULL) {
+ goto done;
+ }
+ dfd = dirfd(d);
+
+ /* Iterate through all files in the directory, executing the
+ * file movements requested there-in.
+ */
+ while ((de = readdir(d))) {
+ const char *name = de->d_name;
+
+ if (de->d_type == DT_DIR) {
+ continue;
+ } else {
+ subfd = openat(dfd, name, O_RDONLY);
+ if (subfd < 0) {
+ LOGW("Unable to open update commands at %s%s\n",
+ UPDATE_COMMANDS_DIR_PREFIX, name);
+ continue;
+ }
+
+ bufp = 0;
+ bufe = 0;
+ buf[PKG_PATH_MAX] = 0;
+ srcpkg[0] = dstpkg[0] = 0;
+ while (1) {
+ bufi = bufp;
+ while (bufi < bufe && buf[bufi] != '\n') {
+ bufi++;
+ }
+ if (bufi < bufe) {
+ buf[bufi] = 0;
+ LOGV("Processing line: %s\n", buf+bufp);
+ hasspace = 0;
+ while (bufp < bufi && isspace(buf[bufp])) {
+ hasspace = 1;
+ bufp++;
+ }
+ if (buf[bufp] == '#' || bufp == bufi) {
+ // skip comments and empty lines.
+ } else if (hasspace) {
+ if (dstpkg[0] == 0) {
+ LOGW("Path before package line in %s%s: %s\n",
+ UPDATE_COMMANDS_DIR_PREFIX, name, buf+bufp);
+ } else if (srcpkg[0] == 0) {
+ // Skip -- source package no longer exists.
+ } else {
+ LOGV("Move file: %s (from %s to %s)\n", buf+bufp, srcpkg, dstpkg);
+ if (!create_move_path(srcpath, PKG_DIR_PREFIX, srcpkg, buf+bufp) &&
+ !create_move_path(dstpath, PKG_DIR_PREFIX, dstpkg, buf+bufp)) {
+ LOGI("Renaming %s to %s (uid %d)\n", srcpath, dstpath, dstuid);
+ mkinnerdirs(dstpath, strlen(dstpath)-(bufi-bufp),
+ S_IRWXU|S_IRWXG|S_IXOTH, dstuid, dstgid);
+ if (rename(srcpath, dstpath) >= 0) {
+ if (chown(dstpath, dstuid, dstgid) < 0) {
+ LOGE("cannot chown %s: %s\n", dstpath, strerror(errno));
+ unlink(dstpath);
+ }
+ } else {
+ LOGW("Unable to rename %s to %s: %s\n",
+ srcpath, dstpath, strerror(errno));
+ }
+ }
+ }
+ } else {
+ char* div = strchr(buf+bufp, ':');
+ if (div == NULL) {
+ LOGW("Bad package spec in %s%s; no ':' sep: %s\n",
+ UPDATE_COMMANDS_DIR_PREFIX, name, buf+bufp);
+ } else {
+ *div = 0;
+ div++;
+ if (strlen(buf+bufp) < PKG_NAME_MAX) {
+ strcpy(dstpkg, buf+bufp);
+ } else {
+ srcpkg[0] = dstpkg[0] = 0;
+ LOGW("Package name too long in %s%s: %s\n",
+ UPDATE_COMMANDS_DIR_PREFIX, name, buf+bufp);
+ }
+ if (strlen(div) < PKG_NAME_MAX) {
+ strcpy(srcpkg, div);
+ } else {
+ srcpkg[0] = dstpkg[0] = 0;
+ LOGW("Package name too long in %s%s: %s\n",
+ UPDATE_COMMANDS_DIR_PREFIX, name, div);
+ }
+ if (srcpkg[0] != 0) {
+ if (!create_pkg_path(srcpath, PKG_DIR_PREFIX, srcpkg,
+ PKG_DIR_POSTFIX)) {
+ if (lstat(srcpath, &s) < 0) {
+ // Package no longer exists -- skip.
+ srcpkg[0] = 0;
+ }
+ } else {
+ srcpkg[0] = 0;
+ LOGW("Can't create path %s in %s%s\n",
+ div, UPDATE_COMMANDS_DIR_PREFIX, name);
+ }
+ if (srcpkg[0] != 0) {
+ if (!create_pkg_path(dstpath, PKG_DIR_PREFIX, dstpkg,
+ PKG_DIR_POSTFIX)) {
+ if (lstat(dstpath, &s) == 0) {
+ dstuid = s.st_uid;
+ dstgid = s.st_gid;
+ } else {
+ srcpkg[0] = 0;
+ LOGW("Can't stat path %s in %s%s: %s\n",
+ dstpath, UPDATE_COMMANDS_DIR_PREFIX,
+ name, strerror(errno));
+ }
+ } else {
+ srcpkg[0] = 0;
+ LOGW("Can't create path %s in %s%s\n",
+ div, UPDATE_COMMANDS_DIR_PREFIX, name);
+ }
+ }
+ LOGV("Transfering from %s to %s: uid=%d\n",
+ srcpkg, dstpkg, dstuid);
+ }
+ }
+ }
+ bufp = bufi+1;
+ } else {
+ if (bufp == 0) {
+ if (bufp < bufe) {
+ LOGW("Line too long in %s%s, skipping: %s\n",
+ UPDATE_COMMANDS_DIR_PREFIX, name, buf);
+ }
+ } else if (bufp < bufe) {
+ memcpy(buf, buf+bufp, bufe-bufp);
+ bufe -= bufp;
+ bufp = 0;
+ }
+ readlen = read(subfd, buf+bufe, PKG_PATH_MAX-bufe);
+ if (readlen < 0) {
+ LOGW("Failure reading update commands in %s%s: %s\n",
+ UPDATE_COMMANDS_DIR_PREFIX, name, strerror(errno));
+ break;
+ } else if (readlen == 0) {
+ break;
+ }
+ bufe += readlen;
+ buf[bufe] = 0;
+ LOGV("Read buf: %s\n", buf);
+ }
+ }
+ close(subfd);
+ }
+ }
+ closedir(d);
+done:
+ return 0;
+}
diff --git a/cmds/installd/installd.c b/cmds/installd/installd.c
index 5bc6c86..882c493 100644
--- a/cmds/installd/installd.c
+++ b/cmds/installd/installd.c
@@ -53,6 +53,11 @@
return uninstall(arg[0], atoi(arg[1])); /* pkgname */
}
+static int do_rename(char **arg, char reply[REPLY_MAX])
+{
+ return renamepkg(arg[0], arg[1], atoi(arg[2])); /* oldpkgname, newpkgname */
+}
+
static int do_free_cache(char **arg, char reply[REPLY_MAX]) /* TODO int:free_size */
{
return free_cache(atoi(arg[0])); /* free_size */
@@ -87,6 +92,11 @@
return delete_user_data(arg[0], atoi(arg[1])); /* pkgname */
}
+static int do_movefiles(char **arg, char reply[REPLY_MAX])
+{
+ return movefiles();
+}
+
struct cmdinfo {
const char *name;
unsigned numargs;
@@ -100,11 +110,13 @@
{ "movedex", 2, do_move_dex },
{ "rmdex", 1, do_rm_dex },
{ "remove", 2, do_remove },
+ { "rename", 3, do_rename },
{ "freecache", 1, do_free_cache },
{ "rmcache", 2, do_rm_cache },
{ "protect", 2, do_protect },
{ "getsize", 4, do_get_size },
{ "rmuserdata", 2, do_rm_user_data },
+ { "movefiles", 0, do_movefiles },
};
static int readx(int s, void *_buf, int count)
diff --git a/cmds/installd/installd.h b/cmds/installd/installd.h
index 35a173e..92ae310 100644
--- a/cmds/installd/installd.h
+++ b/cmds/installd/installd.h
@@ -73,6 +73,7 @@
#define DALVIK_CACHE_PREFIX "/data/dalvik-cache/"
#define DALVIK_CACHE_POSTFIX "/classes.dex"
+#define UPDATE_COMMANDS_DIR_PREFIX "/system/etc/updatecmds/"
#define PKG_NAME_MAX 128 /* largest allowed package name */
#define PKG_PATH_MAX 256 /* max size of any path we use */
@@ -97,6 +98,7 @@
int install(const char *pkgname, int encrypted_fs_flag, uid_t uid, gid_t gid);
int uninstall(const char *pkgname, int encrypted_fs_flag);
+int renamepkg(const char *oldpkgname, const char *newpkgname, int encrypted_fs_flag);
int delete_user_data(const char *pkgname, int encrypted_fs_flag);
int delete_cache(const char *pkgname, int encrypted_fs_flag);
int move_dex(const char *src, const char *dst);
@@ -106,3 +108,4 @@
int *codesize, int *datasize, int *cachesize, int encrypted_fs_flag);
int free_cache(int free_size);
int dexopt(const char *apk_path, uid_t uid, int is_public);
+int movefiles();
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 10fef0d..1af6d6e 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -61,6 +61,7 @@
import android.view.View;
import android.view.ViewDebug;
import android.view.ViewManager;
+import android.view.ViewRoot;
import android.view.Window;
import android.view.WindowManager;
import android.view.WindowManagerImpl;
@@ -1813,6 +1814,7 @@
public static final int DESTROY_BACKUP_AGENT = 129;
public static final int SUICIDE = 130;
public static final int REMOVE_PROVIDER = 131;
+ public static final int ENABLE_JIT = 132;
String codeToString(int code) {
if (localLOGV) {
switch (code) {
@@ -1848,6 +1850,7 @@
case DESTROY_BACKUP_AGENT: return "DESTROY_BACKUP_AGENT";
case SUICIDE: return "SUICIDE";
case REMOVE_PROVIDER: return "REMOVE_PROVIDER";
+ case ENABLE_JIT: return "ENABLE_JIT";
}
}
return "(unknown)";
@@ -1965,6 +1968,9 @@
case REMOVE_PROVIDER:
completeRemoveProvider((IContentProvider)msg.obj);
break;
+ case ENABLE_JIT:
+ ensureJitEnabled();
+ break;
}
}
@@ -2000,6 +2006,7 @@
prev.nextIdle = null;
} while (a != null);
}
+ ensureJitEnabled();
return false;
}
}
@@ -2064,6 +2071,7 @@
String mInstrumentationAppPackage = null;
String mInstrumentedAppDir = null;
boolean mSystemThread = false;
+ boolean mJitEnabled = false;
/**
* Activities that are enqueued to be relaunched. This list is accessed
@@ -2269,6 +2277,13 @@
}
}
+ void ensureJitEnabled() {
+ if (!mJitEnabled) {
+ mJitEnabled = true;
+ dalvik.system.VMRuntime.getRuntime().startJitCompilation();
+ }
+ }
+
void scheduleGcIdler() {
if (!mGcIdlerScheduled) {
mGcIdlerScheduled = true;
@@ -2808,6 +2823,7 @@
ActivityManagerNative.getDefault().serviceDoneExecuting(
data.token, 0, 0, 0);
}
+ ensureJitEnabled();
} catch (RemoteException ex) {
}
} catch (Exception e) {
@@ -2876,6 +2892,7 @@
} catch (RemoteException e) {
// nothing to do.
}
+ ensureJitEnabled();
} catch (Exception e) {
if (!mInstrumentation.onException(s, e)) {
throw new RuntimeException(
@@ -3864,10 +3881,6 @@
mBoundApplication = data;
mConfiguration = new Configuration(data.config);
- // We now rely on this being set by zygote.
- //Process.setGid(data.appInfo.gid);
- //Process.setUid(data.appInfo.uid);
-
// send up app name; do this *before* waiting for debugger
Process.setArgV0(data.processName);
android.ddm.DdmHandleAppName.setAppName(data.processName);
@@ -3998,6 +4011,9 @@
List<ProviderInfo> providers = data.providers;
if (providers != null) {
installContentProviders(app, providers);
+ // For process that contain content providers, we want to
+ // ensure that the JIT is enabled "at some point".
+ mH.sendEmptyMessageDelayed(H.ENABLE_JIT, 10*1000);
}
try {
@@ -4303,6 +4319,11 @@
sThreadLocal.set(this);
mSystemThread = system;
if (!system) {
+ ViewRoot.addFirstDrawHandler(new Runnable() {
+ public void run() {
+ ensureJitEnabled();
+ }
+ });
android.ddm.DdmHandleAppName.setAppName("<pre-initialized>");
RuntimeInit.setApplicationObject(mAppThread.asBinder());
IActivityManager mgr = ActivityManagerNative.getDefault();
diff --git a/core/java/android/app/SearchDialog.java b/core/java/android/app/SearchDialog.java
index ec9f3b4..7fa5b08 100644
--- a/core/java/android/app/SearchDialog.java
+++ b/core/java/android/app/SearchDialog.java
@@ -60,6 +60,7 @@
import android.widget.AutoCompleteTextView;
import android.widget.Button;
import android.widget.ImageButton;
+import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.TextView;
@@ -105,7 +106,7 @@
// views & widgets
private TextView mBadgeLabel;
- private SearchSourceSelector mSourceSelector;
+ private ImageView mAppIcon;
private SearchAutoComplete mSearchAutoComplete;
private Button mGoButton;
private ImageButton mVoiceButton;
@@ -209,8 +210,7 @@
mBadgeLabel = (TextView) findViewById(com.android.internal.R.id.search_badge);
mSearchAutoComplete = (SearchAutoComplete)
findViewById(com.android.internal.R.id.search_src_text);
- mSourceSelector = new SearchSourceSelector(
- findViewById(com.android.internal.R.id.search_source_selector));
+ mAppIcon = (ImageView) findViewById(com.android.internal.R.id.search_app_icon);
mGoButton = (Button) findViewById(com.android.internal.R.id.search_go_btn);
mVoiceButton = (ImageButton) findViewById(com.android.internal.R.id.search_voice_btn);
mSearchPlate = findViewById(com.android.internal.R.id.search_plate);
@@ -609,16 +609,13 @@
}
private void updateSearchAppIcon() {
- mSourceSelector.setSource(mSearchable.getSearchActivity());
- mSourceSelector.setAppSearchData(mAppSearchData);
-
// In Donut, we special-case the case of the browser to hide the app icon as if it were
// global search, for extra space for url entry.
//
// TODO: Remove this special case once the issue has been reconciled in Eclair.
if (mGlobalSearchMode || isBrowserSearch()) {
- mSourceSelector.setSourceIcon(null);
- mSourceSelector.setVisibility(View.GONE);
+ mAppIcon.setImageResource(0);
+ mAppIcon.setVisibility(View.GONE);
mSearchPlate.setPadding(SEARCH_PLATE_LEFT_PADDING_GLOBAL,
mSearchPlate.getPaddingTop(),
mSearchPlate.getPaddingRight(),
@@ -634,8 +631,8 @@
icon = pm.getDefaultActivityIcon();
Log.w(LOG_TAG, mLaunchComponent + " not found, using generic app icon");
}
- mSourceSelector.setSourceIcon(icon);
- mSourceSelector.setVisibility(View.VISIBLE);
+ mAppIcon.setImageDrawable(icon);
+ mAppIcon.setVisibility(View.VISIBLE);
mSearchPlate.setPadding(SEARCH_PLATE_LEFT_PADDING_NON_GLOBAL,
mSearchPlate.getPaddingTop(),
mSearchPlate.getPaddingRight(),
@@ -818,7 +815,6 @@
if (!mSearchAutoComplete.isPerformingCompletion()) {
// The user changed the query, remember it.
mUserQuery = s == null ? "" : s.toString();
- mSourceSelector.setQuery(mUserQuery);
}
}
@@ -1932,7 +1928,6 @@
query = "";
}
mUserQuery = query;
- mSourceSelector.setQuery(query);
mSearchAutoComplete.setText(query);
mSearchAutoComplete.setSelection(query.length());
}
diff --git a/core/java/android/app/SearchManager.java b/core/java/android/app/SearchManager.java
index 12a4347..3046a2c 100644
--- a/core/java/android/app/SearchManager.java
+++ b/core/java/android/app/SearchManager.java
@@ -1604,15 +1604,6 @@
public final static String SUGGEST_PARAMETER_LIMIT = "limit";
/**
- * Intent action for opening the search source selection activity.
- * The intent may include these extra values:
- * {@link #QUERY},
- * {@link #APP_DATA}.
- */
- public static final String INTENT_ACTION_SELECT_SEARCH_SOURCE
- = "android.intent.action.SELECT_SEARCH_SOURCE";
-
- /**
* If a suggestion has this value in {@link #SUGGEST_COLUMN_INTENT_ACTION},
* the search dialog will switch to a different suggestion source when the
* suggestion is clicked.
diff --git a/core/java/android/app/SearchSourceSelector.java b/core/java/android/app/SearchSourceSelector.java
deleted file mode 100644
index fabf858..0000000
--- a/core/java/android/app/SearchSourceSelector.java
+++ /dev/null
@@ -1,197 +0,0 @@
-/*
- * 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.app;
-
-import com.android.internal.R;
-
-import android.content.ActivityNotFoundException;
-import android.content.ComponentName;
-import android.content.ContentResolver;
-import android.content.Intent;
-import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
-import android.net.Uri;
-import android.os.Bundle;
-import android.text.TextUtils;
-import android.util.Log;
-import android.view.View;
-import android.widget.ImageButton;
-
-import java.util.List;
-
-/**
- * Utilities for setting up the search source selector.
- *
- * This class has two copies:
- * android.app.SearchSourceSelector
- * com.android.quicksearchbox.ui.SearchSourceSelector
- *
- * They should keep the same look and feel as much as possible,
- * but only the intent details must absolutely stay in sync.
- *
- * @hide
- */
-public class SearchSourceSelector implements View.OnClickListener {
-
- private static final String TAG = "SearchSourceSelector";
-
- // TODO: This should be defined in android.provider.Applications,
- // and have a less made-up value.
- private static final String APPLICATION_TYPE = "application/vnd.android.application";
-
- public static final int ICON_VIEW_ID = R.id.search_source_selector_icon;
-
- private final View mView;
-
- private final ImageButton mIconView;
-
- private ComponentName mSource;
-
- private Bundle mAppSearchData;
-
- private String mQuery;
-
- public SearchSourceSelector(View view) {
- mView = view;
- mIconView = (ImageButton) view.findViewById(ICON_VIEW_ID);
- mIconView.setOnClickListener(this);
- }
-
- /**
- * Sets the icon displayed in the search source selector.
- */
- public void setSourceIcon(Drawable icon) {
- mIconView.setImageDrawable(icon);
- }
-
- /**
- * Sets the current search source.
- */
- public void setSource(ComponentName source) {
- mSource = source;
- }
-
- /**
- * Sets the app-specific data that will be passed to the search activity if
- * the user opens the source selector and chooses a source.
- */
- public void setAppSearchData(Bundle appSearchData) {
- mAppSearchData = appSearchData;
- }
-
- /**
- * Sets the initial query that will be passed to the search activity if
- * the user opens the source selector and chooses a source.
- */
- public void setQuery(String query) {
- mQuery = query;
- }
-
- public void setVisibility(int visibility) {
- mView.setVisibility(visibility);
- }
-
- /**
- * Creates an intent for opening the search source selector activity.
- *
- * @param source The current search source.
- * @param query The initial query that will be passed to the search activity if
- * the user opens the source selector and chooses a source.
- * @param appSearchData The app-specific data that will be passed to the search
- * activity if the user opens the source selector and chooses a source.
- */
- public static Intent createIntent(ComponentName source, String query, Bundle appSearchData) {
- Intent intent = new Intent(SearchManager.INTENT_ACTION_SELECT_SEARCH_SOURCE);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
- | Intent.FLAG_ACTIVITY_CLEAR_TOP
- | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
- Uri sourceUri = componentNameToUri(source);
- if (sourceUri != null) {
- intent.setDataAndType(sourceUri, APPLICATION_TYPE);
- }
- if (query != null) {
- intent.putExtra(SearchManager.QUERY, query);
- }
- if (query != null) {
- intent.putExtra(SearchManager.APP_DATA, appSearchData);
- }
- return intent;
- }
-
- /**
- * Gets the search source from which the given
- * {@link SearchManager.INTENT_ACTION_SELECT_SEARCH_SOURCE} intent was sent.
- */
- public static ComponentName getSource(Intent intent) {
- return uriToComponentName(intent.getData());
- }
-
- private static Uri componentNameToUri(ComponentName name) {
- if (name == null) return null;
- // TODO: This URI format is specificed in android.provider.Applications which is @hidden
- return new Uri.Builder()
- .scheme(ContentResolver.SCHEME_CONTENT)
- .authority("applications")
- .appendEncodedPath("applications")
- .appendPath(name.getPackageName())
- .appendPath(name.getClassName())
- .build();
- }
-
- private static ComponentName uriToComponentName(Uri uri) {
- if (uri == null) return null;
- List<String> path = uri.getPathSegments();
- if (path == null || path.size() != 3) return null;
- String pkg = path.get(1);
- String cls = path.get(2);
- if (TextUtils.isEmpty(pkg) || TextUtils.isEmpty(cls)) return null;
- return new ComponentName(pkg, cls);
- }
-
- public void onClick(View v) {
- trigger();
- }
-
- private void trigger() {
- try {
- Intent intent = createIntent(mSource, mQuery, mAppSearchData);
- intent.setSourceBounds(getOnScreenRect(mIconView));
- mIconView.getContext().startActivity(intent);
- } catch (ActivityNotFoundException ex) {
- Log.e(TAG, "No source selector activity found", ex);
- }
- }
-
- // TODO: This code is replicated in lots of places:
- // - android.provider.ContactsContract.QuickContact.showQuickContact()
- // - android.widget.RemoteViews.setOnClickPendingIntent()
- // - com.android.launcher2.Launcher.onClick()
- // - com.android.launcher.Launcher.onClick()
- // - com.android.server.status.StatusBarService.Launcher.onClick()
- private static Rect getOnScreenRect(View v) {
- final float appScale = v.getResources().getCompatibilityInfo().applicationScale;
- final int[] pos = new int[2];
- v.getLocationOnScreen(pos);
- final Rect rect = new Rect();
- rect.left = (int) (pos[0] * appScale + 0.5f);
- rect.top = (int) (pos[1] * appScale + 0.5f);
- rect.right = (int) ((pos[0] + v.getWidth()) * appScale + 0.5f);
- rect.bottom = (int) ((pos[1] + v.getHeight()) * appScale + 0.5f);
- return rect;
- }
-
-}
diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java
index 51d7393..72ec616 100644
--- a/core/java/android/app/StatusBarManager.java
+++ b/core/java/android/app/StatusBarManager.java
@@ -31,12 +31,12 @@
public class StatusBarManager {
/**
* Flag for {@link #disable} to make the status bar not expandable. Unless you also
- * set {@link #DISABLE_NOTIFICATIONS}, new notifications will continue to show.
+ * set {@link #DISABLE_NOTIFICATION_ICONS}, new notifications will continue to show.
*/
public static final int DISABLE_EXPAND = 0x00000001;
/**
- * Flag for {@link #disable} to hide notification icons and ticker text.
+ * Flag for {@link #disable} to hide notification icons and scrolling ticker text.
*/
public static final int DISABLE_NOTIFICATION_ICONS = 0x00000002;
@@ -47,6 +47,12 @@
public static final int DISABLE_NOTIFICATION_ALERTS = 0x00000004;
/**
+ * Flag for {@link #disable} to hide only the scrolling ticker. Note that
+ * {@link #DISABLE_NOTIFICATION_ICONS} implies {@link #DISABLE_NOTIFICATION_TICKER}.
+ */
+ public static final int DISABLE_NOTIFICATION_TICKER = 0x00000008;
+
+ /**
* Re-enable all of the status bar features that you've disabled.
*/
public static final int DISABLE_NONE = 0x00000000;
diff --git a/core/java/android/backup/IBackupManager.aidl b/core/java/android/backup/IBackupManager.aidl
index cb775f7..bf6c79f 100644
--- a/core/java/android/backup/IBackupManager.aidl
+++ b/core/java/android/backup/IBackupManager.aidl
@@ -73,6 +73,21 @@
void setBackupEnabled(boolean isEnabled);
/**
+ * Enable/disable automatic restore of application data at install time. When
+ * enabled, installation of any package will involve the Backup Manager. If data
+ * exists for the newly-installed package, either from the device's current [enabled]
+ * backup dataset or from the restore set used in the last wholesale restore operation,
+ * that data will be supplied to the new package's restore agent before the package
+ * is made generally available for launch.
+ *
+ * <p>Callers must hold the android.permission.BACKUP permission to use this method.
+ *
+ * @param doAutoRestore When true, enables the automatic app-data restore facility. When
+ * false, this facility will be disabled.
+ */
+ void setAutoRestore(boolean doAutoRestore);
+
+ /**
* Indicate that any necessary one-time provisioning has occurred.
*
* <p>Callers must hold the android.permission.BACKUP permission to use this method.
diff --git a/core/java/android/backup/IRestoreSession.aidl b/core/java/android/backup/IRestoreSession.aidl
index fd40d98..bead395 100644
--- a/core/java/android/backup/IRestoreSession.aidl
+++ b/core/java/android/backup/IRestoreSession.aidl
@@ -40,6 +40,8 @@
* Restore the given set onto the device, replacing the current data of any app
* contained in the restore set with the data previously backed up.
*
+ * <p>Callers must hold the android.permission.BACKUP permission to use this method.
+ *
* @return Zero on success; nonzero on error. The observer will only receive
* progress callbacks if this method returned zero.
* @param token The token from {@link getAvailableRestoreSets()} corresponding to
@@ -47,7 +49,24 @@
* @param observer If non-null, this binder points to an object that will receive
* progress callbacks during the restore operation.
*/
- int performRestore(long token, IRestoreObserver observer);
+ int restoreAll(long token, IRestoreObserver observer);
+
+ /**
+ * Restore a single application from backup. The data will be restored from the
+ * current backup dataset if the given package has stored data there, or from
+ * the dataset used during the last full device setup operation if the current
+ * backup dataset has no matching data. If no backup data exists for this package
+ * in either source, a nonzero value will be returned.
+ *
+ * @return Zero on success; nonzero on error. The observer will only receive
+ * progress callbacks if this method returned zero.
+ * @param packageName The name of the package whose data to restore. If this is
+ * not the name of the caller's own package, then the android.permission.BACKUP
+ * permission must be held.
+ * @param observer If non-null, this binder points to an object that will receive
+ * progress callbacks during the restore operation.
+ */
+ int restorePackage(in String packageName, IRestoreObserver observer);
/**
* End this restore session. After this method is called, the IRestoreSession binder
diff --git a/core/java/android/backup/RestoreSession.java b/core/java/android/backup/RestoreSession.java
index 6b35fe8..d10831e 100644
--- a/core/java/android/backup/RestoreSession.java
+++ b/core/java/android/backup/RestoreSession.java
@@ -58,25 +58,56 @@
* Restore the given set onto the device, replacing the current data of any app
* contained in the restore set with the data previously backed up.
*
+ * <p>Callers must hold the android.permission.BACKUP permission to use this method.
+ *
* @return Zero on success; nonzero on error. The observer will only receive
* progress callbacks if this method returned zero.
- * @param token The token from {@link #getAvailableRestoreSets()} corresponding to
+ * @param token The token from {@link getAvailableRestoreSets()} corresponding to
* the restore set that should be used.
- * @param observer If non-null, this argument points to an object that will receive
- * progress callbacks during the restore operation. These callbacks will occur
- * on the main thread of the application.
+ * @param observer If non-null, this binder points to an object that will receive
+ * progress callbacks during the restore operation.
*/
- public int performRestore(long token, RestoreObserver observer) {
+ public int restoreAll(long token, RestoreObserver observer) {
int err = -1;
if (mObserver != null) {
- Log.d(TAG, "performRestore() called during active restore");
+ Log.d(TAG, "restoreAll() called during active restore");
return -1;
}
mObserver = new RestoreObserverWrapper(mContext, observer);
try {
- err = mBinder.performRestore(token, mObserver);
+ err = mBinder.restoreAll(token, mObserver);
} catch (RemoteException e) {
- Log.d(TAG, "Can't contact server to perform restore");
+ Log.d(TAG, "Can't contact server to restore");
+ }
+ return err;
+ }
+
+ /**
+ * Restore a single application from backup. The data will be restored from the
+ * current backup dataset if the given package has stored data there, or from
+ * the dataset used during the last full device setup operation if the current
+ * backup dataset has no matching data. If no backup data exists for this package
+ * in either source, a nonzero value will be returned.
+ *
+ * @return Zero on success; nonzero on error. The observer will only receive
+ * progress callbacks if this method returned zero.
+ * @param packageName The name of the package whose data to restore. If this is
+ * not the name of the caller's own package, then the android.permission.BACKUP
+ * permission must be held.
+ * @param observer If non-null, this binder points to an object that will receive
+ * progress callbacks during the restore operation.
+ */
+ public int restorePackage(String packageName, RestoreObserver observer) {
+ int err = -1;
+ if (mObserver != null) {
+ Log.d(TAG, "restorePackage() called during active restore");
+ return -1;
+ }
+ mObserver = new RestoreObserverWrapper(mContext, observer);
+ try {
+ err = mBinder.restorePackage(packageName, mObserver);
+ } catch (RemoteException e) {
+ Log.d(TAG, "Can't contact server to restore package");
}
return err;
}
@@ -87,7 +118,7 @@
*
* <p><b>Note:</b> The caller <i>must</i> invoke this method to end the restore session,
* even if {@link #getAvailableRestoreSets()} or
- * {@link #performRestore(long, RestoreObserver)} failed.
+ * {@link #restorePackage(long, String, RestoreObserver)} failed.
*/
public void endRestoreSession() {
try {
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index bbde0a6..bac55cc 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -953,6 +953,39 @@
return null;
}
+ } else if (tagName.equals("original-package")) {
+ sa = res.obtainAttributes(attrs,
+ com.android.internal.R.styleable.AndroidManifestOriginalPackage);
+
+ String name = sa.getNonResourceString(
+ com.android.internal.R.styleable.AndroidManifestOriginalPackage_name);
+
+ sa.recycle();
+
+ if (name != null && (flags&PARSE_IS_SYSTEM) != 0) {
+ pkg.mOriginalPackage = name;
+ }
+
+ XmlUtils.skipCurrentTag(parser);
+
+ } else if (tagName.equals("adopt-permissions")) {
+ sa = res.obtainAttributes(attrs,
+ com.android.internal.R.styleable.AndroidManifestOriginalPackage);
+
+ String name = sa.getNonResourceString(
+ com.android.internal.R.styleable.AndroidManifestOriginalPackage_name);
+
+ sa.recycle();
+
+ if (name != null && (flags&PARSE_IS_SYSTEM) != 0) {
+ if (pkg.mAdoptPermissions == null) {
+ pkg.mAdoptPermissions = new ArrayList<String>();
+ }
+ pkg.mAdoptPermissions.add(name);
+ }
+
+ XmlUtils.skipCurrentTag(parser);
+
} else if (tagName.equals("eat-comment")) {
// Just skip this tag
XmlUtils.skipCurrentTag(parser);
@@ -2528,6 +2561,9 @@
public ArrayList<String> usesOptionalLibraries = null;
public String[] usesLibraryFiles = null;
+ public String mOriginalPackage = null;
+ public ArrayList<String> mAdoptPermissions = null;
+
// We store the application meta-data independently to avoid multiple unwanted references
public Bundle mAppMetaData = null;
diff --git a/core/java/android/gesture/Gesture.java b/core/java/android/gesture/Gesture.java
index d71344c..300cd28 100755
--- a/core/java/android/gesture/Gesture.java
+++ b/core/java/android/gesture/Gesture.java
@@ -293,7 +293,7 @@
} catch (IOException e) {
Log.e(GestureConstants.LOG_TAG, "Error reading Gesture from parcel:", e);
} finally {
- GestureUtilities.closeStream(inStream);
+ GestureUtils.closeStream(inStream);
}
if (gesture != null) {
@@ -322,8 +322,8 @@
} catch (IOException e) {
Log.e(GestureConstants.LOG_TAG, "Error writing Gesture to parcel:", e);
} finally {
- GestureUtilities.closeStream(outStream);
- GestureUtilities.closeStream(byteStream);
+ GestureUtils.closeStream(outStream);
+ GestureUtils.closeStream(byteStream);
}
if (result) {
diff --git a/core/java/android/gesture/GestureOverlayView.java b/core/java/android/gesture/GestureOverlayView.java
index 30ecf5a..b6c260f 100755
--- a/core/java/android/gesture/GestureOverlayView.java
+++ b/core/java/android/gesture/GestureOverlayView.java
@@ -638,7 +638,7 @@
if (mTotalLength > mGestureStrokeLengthThreshold) {
final OrientedBoundingBox box =
- GestureUtilities.computeOrientedBoundingBox(mStrokeBuffer);
+ GestureUtils.computeOrientedBoundingBox(mStrokeBuffer);
float angle = Math.abs(box.orientation);
if (angle > 90) {
diff --git a/core/java/android/gesture/GestureStore.java b/core/java/android/gesture/GestureStore.java
index 11a94d1..11b5044 100644
--- a/core/java/android/gesture/GestureStore.java
+++ b/core/java/android/gesture/GestureStore.java
@@ -264,7 +264,7 @@
mChanged = false;
} finally {
- if (closeStream) GestureUtilities.closeStream(out);
+ if (closeStream) GestureUtils.closeStream(out);
}
}
@@ -299,7 +299,7 @@
Log.d(LOG_TAG, "Loading gestures library = " + (end - start) + " ms");
}
} finally {
- if (closeStream) GestureUtilities.closeStream(in);
+ if (closeStream) GestureUtils.closeStream(in);
}
}
diff --git a/core/java/android/gesture/GestureStroke.java b/core/java/android/gesture/GestureStroke.java
index c3ddb28..1d0f0fe 100644
--- a/core/java/android/gesture/GestureStroke.java
+++ b/core/java/android/gesture/GestureStroke.java
@@ -159,15 +159,15 @@
* @return the path
*/
public Path toPath(float width, float height, int numSample) {
- final float[] pts = GestureUtilities.temporalSampling(this, numSample);
+ final float[] pts = GestureUtils.temporalSampling(this, numSample);
final RectF rect = boundingBox;
- GestureUtilities.translate(pts, -rect.left, -rect.top);
+ GestureUtils.translate(pts, -rect.left, -rect.top);
float sx = width / rect.width();
float sy = height / rect.height();
float scale = sx > sy ? sy : sx;
- GestureUtilities.scale(pts, scale, scale);
+ GestureUtils.scale(pts, scale, scale);
float mX = 0;
float mY = 0;
@@ -241,6 +241,6 @@
* @return OrientedBoundingBox
*/
public OrientedBoundingBox computeOrientedBoundingBox() {
- return GestureUtilities.computeOrientedBoundingBox(points);
+ return GestureUtils.computeOrientedBoundingBox(points);
}
}
diff --git a/core/java/android/gesture/GestureUtilities.java b/core/java/android/gesture/GestureUtils.java
similarity index 99%
rename from core/java/android/gesture/GestureUtilities.java
rename to core/java/android/gesture/GestureUtils.java
index 9d95ce4..dd221fc 100755
--- a/core/java/android/gesture/GestureUtilities.java
+++ b/core/java/android/gesture/GestureUtils.java
@@ -36,12 +36,12 @@
* distances between two gestures).
* </ul>
*/
-public final class GestureUtilities {
+public final class GestureUtils {
private static final float SCALING_THRESHOLD = 0.26f;
private static final float NONUNIFORM_SCALE = (float) Math.sqrt(2);
- private GestureUtilities() {
+ private GestureUtils() {
}
/**
diff --git a/core/java/android/gesture/Instance.java b/core/java/android/gesture/Instance.java
index bb0b340..02a6519 100755
--- a/core/java/android/gesture/Instance.java
+++ b/core/java/android/gesture/Instance.java
@@ -84,13 +84,13 @@
}
private static float[] spatialSampler(Gesture gesture) {
- return GestureUtilities.spatialSampling(gesture, PATCH_SAMPLE_SIZE, false);
+ return GestureUtils.spatialSampling(gesture, PATCH_SAMPLE_SIZE, false);
}
private static float[] temporalSampler(int orientationType, Gesture gesture) {
- float[] pts = GestureUtilities.temporalSampling(gesture.getStrokes().get(0),
+ float[] pts = GestureUtils.temporalSampling(gesture.getStrokes().get(0),
SEQUENCE_SAMPLE_SIZE);
- float[] center = GestureUtilities.computeCentroid(pts);
+ float[] center = GestureUtils.computeCentroid(pts);
float orientation = (float)Math.atan2(pts[1] - center[1], pts[0] - center[0]);
float adjustment = -orientation;
@@ -104,8 +104,8 @@
}
}
- GestureUtilities.translate(pts, -center[0], -center[1]);
- GestureUtilities.rotate(pts, adjustment);
+ GestureUtils.translate(pts, -center[0], -center[1]);
+ GestureUtils.rotate(pts, adjustment);
return pts;
}
diff --git a/core/java/android/gesture/InstanceLearner.java b/core/java/android/gesture/InstanceLearner.java
index 9987e69..7224ded 100644
--- a/core/java/android/gesture/InstanceLearner.java
+++ b/core/java/android/gesture/InstanceLearner.java
@@ -53,9 +53,9 @@
}
double distance;
if (sequenceType == GestureStore.SEQUENCE_SENSITIVE) {
- distance = GestureUtilities.minimumCosineDistance(sample.vector, vector, orientationType);
+ distance = GestureUtils.minimumCosineDistance(sample.vector, vector, orientationType);
} else {
- distance = GestureUtilities.squaredEuclideanDistance(sample.vector, vector);
+ distance = GestureUtils.squaredEuclideanDistance(sample.vector, vector);
}
double weight;
if (distance == 0) {
diff --git a/core/java/android/hardware/SensorManager.java b/core/java/android/hardware/SensorManager.java
index 0c6bb1a..8eed9f7 100644
--- a/core/java/android/hardware/SensorManager.java
+++ b/core/java/android/hardware/SensorManager.java
@@ -287,6 +287,7 @@
static private class SensorThread {
Thread mThread;
+ boolean mSensorsReady;
SensorThread() {
// this gets to the sensor module. We can have only one per process.
@@ -299,17 +300,28 @@
}
// must be called with sListeners lock
- void startLocked(ISensorService service) {
+ boolean startLocked(ISensorService service) {
try {
if (mThread == null) {
Bundle dataChannel = service.getDataChannel();
- mThread = new Thread(new SensorThreadRunnable(dataChannel),
- SensorThread.class.getName());
- mThread.start();
+ if (dataChannel != null) {
+ mSensorsReady = false;
+ SensorThreadRunnable runnable = new SensorThreadRunnable(dataChannel);
+ Thread thread = new Thread(runnable, SensorThread.class.getName());
+ thread.start();
+ synchronized (runnable) {
+ while (mSensorsReady == false) {
+ runnable.wait();
+ }
+ }
+ mThread = thread;
+ }
}
} catch (RemoteException e) {
Log.e(TAG, "RemoteException in startLocked: ", e);
+ } catch (InterruptedException e) {
}
+ return mThread == null ? false : true;
}
private class SensorThreadRunnable implements Runnable {
@@ -319,13 +331,9 @@
}
private boolean open() {
- if (mDataChannel == null) {
- Log.e(TAG, "mDataChannel == NULL, exiting");
- synchronized (sListeners) {
- mThread = null;
- }
- return false;
- }
+ // NOTE: this cannot synchronize on sListeners, since
+ // it's held in the main thread at least until we
+ // return from here.
// this thread is guaranteed to be unique
Parcelable[] pfds = mDataChannel.getParcelableArray("fds");
@@ -370,6 +378,12 @@
return;
}
+ synchronized (this) {
+ // we've open the driver, we're ready to open the sensors
+ mSensorsReady = true;
+ this.notify();
+ }
+
while (true) {
// wait for an event
final int sensor = sensors_data_poll(values, status, timestamp);
@@ -907,14 +921,18 @@
String name = sensor.getName();
int handle = sensor.getHandle();
if (l == null) {
+ result = false;
l = new ListenerDelegate(listener, sensor, handler);
- result = mSensorService.enableSensor(l, name, handle, delay);
- if (result) {
- sListeners.add(l);
- sListeners.notify();
- }
+ sListeners.add(l);
if (!sListeners.isEmpty()) {
- sSensorThread.startLocked(mSensorService);
+ result = sSensorThread.startLocked(mSensorService);
+ if (result) {
+ result = mSensorService.enableSensor(l, name, handle, delay);
+ if (!result) {
+ // there was an error, remove the listeners
+ sListeners.remove(l);
+ }
+ }
}
} else {
result = mSensorService.enableSensor(l, name, handle, delay);
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index 30799ec..d435df5 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -116,6 +116,24 @@
"android.net.conn.BACKGROUND_DATA_SETTING_CHANGED";
/**
+ * Broadcast Action: A tetherable connection has come or gone
+ * TODO - finish the doc
+ * @hide
+ */
+ public static final String ACTION_TETHER_STATE_CHANGED =
+ "android.net.conn.TETHER_STATE_CHANGED";
+
+ /**
+ * @hide
+ */
+ public static final String EXTRA_AVAILABLE_TETHER_COUNT = "availableCount";
+
+ /**
+ * @hide
+ */
+ public static final String EXTRA_ACTIVE_TETHER_COUNT = "activeCount";
+
+ /**
* The Default Mobile data connection. When active, all data traffic
* will use this connection by default. Should not coexist with other
* default connections.
@@ -338,4 +356,48 @@
}
mService = service;
}
+
+ /**
+ * {@hide}
+ */
+ public String[] getTetherableIfaces() {
+ try {
+ return mService.getTetherableIfaces();
+ } catch (RemoteException e) {
+ return new String[0];
+ }
+ }
+
+ /**
+ * {@hide}
+ */
+ public String[] getTetheredIfaces() {
+ try {
+ return mService.getTetheredIfaces();
+ } catch (RemoteException e) {
+ return new String[0];
+ }
+ }
+
+ /**
+ * {@hide}
+ */
+ public boolean tether(String iface) {
+ try {
+ return mService.tether(iface);
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+
+ /**
+ * {@hide}
+ */
+ public boolean untether(String iface) {
+ try {
+ return mService.untether(iface);
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
}
diff --git a/core/java/android/net/IConnectivityManager.aidl b/core/java/android/net/IConnectivityManager.aidl
index 9f59cce..caa3f2b 100644
--- a/core/java/android/net/IConnectivityManager.aidl
+++ b/core/java/android/net/IConnectivityManager.aidl
@@ -50,4 +50,12 @@
boolean getBackgroundDataSetting();
void setBackgroundDataSetting(boolean allowBackgroundData);
+
+ boolean tether(String iface);
+
+ boolean untether(String iface);
+
+ String[] getTetherableIfaces();
+
+ String[] getTetheredIfaces();
}
diff --git a/core/java/android/net/TrafficStats.java b/core/java/android/net/TrafficStats.java
index 62e9f1f..ad8e2bf 100644
--- a/core/java/android/net/TrafficStats.java
+++ b/core/java/android/net/TrafficStats.java
@@ -54,7 +54,7 @@
* @return number of packets. If the statistics are not supported by this device,
* {@link #UNSUPPORTED} will be returned.
*/
- public static long getMobileTxPkts() {
+ public static long getMobileTxPackets() {
return getMobileStat(MOBILE_TX_PACKETS);
}
@@ -64,7 +64,7 @@
* @return number of packets. If the statistics are not supported by this device,
* {@link #UNSUPPORTED} will be returned.
*/
- public static long getMobileRxPkts() {
+ public static long getMobileRxPackets() {
return getMobileStat(MOBILE_RX_PACKETS);
}
@@ -94,7 +94,7 @@
* @return the number of packets. If the statistics are not supported by this device,
* {@link #UNSUPPORTED} will be returned.
*/
- public static long getTotalTxPkts() {
+ public static long getTotalTxPackets() {
return getTotalStat("tx_packets");
}
@@ -104,7 +104,7 @@
* @return number of packets. If the statistics are not supported by this device,
* {@link #UNSUPPORTED} will be returned.
*/
- public static long getTotalRxPkts() {
+ public static long getTotalRxPackets() {
return getTotalStat("rx_packets");
}
diff --git a/core/java/android/os/INetworkManagementService.aidl b/core/java/android/os/INetworkManagementService.aidl
index e4ec098..f48f45f 100644
--- a/core/java/android/os/INetworkManagementService.aidl
+++ b/core/java/android/os/INetworkManagementService.aidl
@@ -140,7 +140,8 @@
* Attaches a PPP server daemon to the specified TTY with the specified
* local/remote addresses.
*/
- void attachPppd(String tty, String localAddr, String remoteAddr);
+ void attachPppd(String tty, String localAddr, String remoteAddr, String dns1Addr,
+ String dns2Addr);
/**
* Detaches a PPP server daemon from the specified TTY.
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 23a9f49..bacaf43 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -2425,6 +2425,14 @@
public static final String BACKUP_ENABLED = "backup_enabled";
/**
+ * Controls whether application data is automatically restored from backup
+ * at install time.
+ * Type: int ( 0 = disabled, 1 = enabled )
+ * @hide
+ */
+ public static final String BACKUP_AUTO_RESTORE = "backup_auto_restore";
+
+ /**
* Indicates whether settings backup has been fully provisioned.
* Type: int ( 0 = unprovisioned, 1 = fully provisioned )
* @hide
@@ -2936,6 +2944,13 @@
public static final String MOUNT_UMS_NOTIFY_ENABLED = "mount_ums_notify_enabled";
/**
+ * Whether or not a notification is displayed when a Tetherable interface is detected.
+ * (0 = false, 1 = true)
+ * @hide
+ */
+ public static final String TETHER_NOTIFY = "tether_notify";
+
+ /**
* If nonzero, ANRs in invisible background processes bring up a dialog.
* Otherwise, the process will be silently killed.
* @hide
diff --git a/core/java/android/text/util/Rfc822Tokenizer.java b/core/java/android/text/util/Rfc822Tokenizer.java
index cb39f7d..9d8bfd9 100644
--- a/core/java/android/text/util/Rfc822Tokenizer.java
+++ b/core/java/android/text/util/Rfc822Tokenizer.java
@@ -41,7 +41,6 @@
* It will try to be tolerant of broken syntax instead of
* returning an error.
*
- * @hide
*/
public static void tokenize(CharSequence text, Collection<Rfc822Token> out) {
StringBuilder name = new StringBuilder();
diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/ViewRoot.java
index 094b7dd..07b2d1c 100644
--- a/core/java/android/view/ViewRoot.java
+++ b/core/java/android/view/ViewRoot.java
@@ -98,6 +98,9 @@
static final ThreadLocal<RunQueue> sRunQueues = new ThreadLocal<RunQueue>();
+ static final ArrayList<Runnable> sFirstDrawHandlers = new ArrayList<Runnable>();
+ static boolean sFirstDrawComplete = false;
+
private static int sDrawTime;
long mLastTrackballTime = 0;
@@ -254,6 +257,14 @@
return sInstanceCount;
}
+ public static void addFirstDrawHandler(Runnable callback) {
+ synchronized (sFirstDrawHandlers) {
+ if (!sFirstDrawComplete) {
+ sFirstDrawHandlers.add(callback);
+ }
+ }
+ }
+
// FIXME for perf testing only
private boolean mProfile = false;
@@ -1189,6 +1200,15 @@
return;
}
+ if (!sFirstDrawComplete) {
+ synchronized (sFirstDrawHandlers) {
+ sFirstDrawComplete = true;
+ for (int i=0; i<sFirstDrawHandlers.size(); i++) {
+ post(sFirstDrawHandlers.get(i));
+ }
+ }
+ }
+
scrollToRectOrFocus(null, false);
if (mAttachInfo.mViewScrollChanged) {
diff --git a/core/java/android/webkit/CacheLoader.java b/core/java/android/webkit/CacheLoader.java
index de8f888..aeb537c 100644
--- a/core/java/android/webkit/CacheLoader.java
+++ b/core/java/android/webkit/CacheLoader.java
@@ -43,7 +43,7 @@
protected boolean setupStreamAndSendStatus() {
mDataStream = mCacheResult.inStream;
mContentLength = mCacheResult.contentLength;
- mHandler.status(1, 1, mCacheResult.httpStatusCode, "OK");
+ mLoadListener.status(1, 1, mCacheResult.httpStatusCode, "OK");
return true;
}
diff --git a/core/java/android/webkit/CacheManager.java b/core/java/android/webkit/CacheManager.java
index 213eaa5..87cab3c 100644
--- a/core/java/android/webkit/CacheManager.java
+++ b/core/java/android/webkit/CacheManager.java
@@ -30,6 +30,8 @@
import java.util.ArrayList;
import java.util.Map;
+import com.android.common.HttpDateTime;
+
import org.bouncycastle.crypto.Digest;
import org.bouncycastle.crypto.digests.SHA1Digest;
diff --git a/core/java/android/webkit/CallbackProxy.java b/core/java/android/webkit/CallbackProxy.java
index 6790c5d..61a2d2ef 100644
--- a/core/java/android/webkit/CallbackProxy.java
+++ b/core/java/android/webkit/CallbackProxy.java
@@ -651,7 +651,42 @@
String message = msg.getData().getString("message");
String sourceID = msg.getData().getString("sourceID");
int lineNumber = msg.getData().getInt("lineNumber");
- mWebChromeClient.onConsoleMessage(message, lineNumber, sourceID);
+ int msgLevel = msg.getData().getInt("msgLevel");
+ int numberOfMessageLevels = ConsoleMessage.MessageLevel.values().length;
+ // Sanity bounds check as we'll index an array with msgLevel
+ if (msgLevel < 0 || msgLevel >= numberOfMessageLevels) {
+ msgLevel = 0;
+ }
+
+ ConsoleMessage.MessageLevel messageLevel =
+ ConsoleMessage.MessageLevel.values()[msgLevel];
+
+ if (!mWebChromeClient.onConsoleMessage(new ConsoleMessage(message, sourceID,
+ lineNumber, messageLevel))) {
+ // If false was returned the user did not provide their own console function so
+ // we should output some default messages to the system log.
+ String logTag = "Web Console";
+ String logMessage = message + " at " + sourceID + ":" + lineNumber;
+
+ switch (messageLevel) {
+ case TIP:
+ Log.v(logTag, logMessage);
+ break;
+ case LOG:
+ Log.i(logTag, logMessage);
+ break;
+ case WARNING:
+ Log.w(logTag, logMessage);
+ break;
+ case ERROR:
+ Log.e(logTag, logMessage);
+ break;
+ case DEBUG:
+ Log.d(logTag, logMessage);
+ break;
+ }
+ }
+
break;
case GET_VISITED_HISTORY:
@@ -1286,8 +1321,10 @@
* occurred.
* @param sourceID The filename of the source file in which the error
* occurred.
+ * @param msgLevel The message level, corresponding to the MessageLevel enum in
+ * WebCore/page/Console.h
*/
- public void addMessageToConsole(String message, int lineNumber, String sourceID) {
+ public void addMessageToConsole(String message, int lineNumber, String sourceID, int msgLevel) {
if (mWebChromeClient == null) {
return;
}
@@ -1296,6 +1333,7 @@
msg.getData().putString("message", message);
msg.getData().putString("sourceID", sourceID);
msg.getData().putInt("lineNumber", lineNumber);
+ msg.getData().putInt("msgLevel", msgLevel);
sendMessage(msg);
}
diff --git a/core/java/android/webkit/ConsoleMessage.java b/core/java/android/webkit/ConsoleMessage.java
new file mode 100644
index 0000000..a9c351a
--- /dev/null
+++ b/core/java/android/webkit/ConsoleMessage.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.webkit;
+
+/**
+ * Public class representing a JavaScript console message from WebCore. This could be a issued
+ * by a call to one of the <code>console</code> logging functions (e.g.
+ * <code>console.log('...')</code>) or a JavaScript error on the page. To receive notifications
+ * of these messages, override the
+ * {@link WebChromeClient#onConsoleMessage(ConsoleMessage)} function.
+ */
+public class ConsoleMessage {
+
+ // This must be kept in sync with the WebCore enum in WebCore/page/Console.h
+ public enum MessageLevel {
+ TIP,
+ LOG,
+ WARNING,
+ ERROR,
+ DEBUG
+ };
+
+ private MessageLevel mLevel;
+ private String mMessage;
+ private String mSourceId;
+ private int mLineNumber;
+
+ public ConsoleMessage(String message, String sourceId, int lineNumber, MessageLevel msgLevel) {
+ mMessage = message;
+ mSourceId = sourceId;
+ mLineNumber = lineNumber;
+ mLevel = msgLevel;
+ }
+
+ public MessageLevel messageLevel() {
+ return mLevel;
+ }
+
+ public String message() {
+ return mMessage;
+ }
+
+ public String sourceId() {
+ return mSourceId;
+ }
+
+ public int lineNumber() {
+ return mLineNumber;
+ }
+};
diff --git a/core/java/android/webkit/ContentLoader.java b/core/java/android/webkit/ContentLoader.java
index 5eb54b0..d13210aa 100644
--- a/core/java/android/webkit/ContentLoader.java
+++ b/core/java/android/webkit/ContentLoader.java
@@ -16,14 +16,10 @@
package android.webkit;
-import android.content.Context;
import android.net.http.EventHandler;
import android.net.http.Headers;
import android.net.Uri;
-import java.io.File;
-import java.io.FileInputStream;
-
/**
* This class is a concrete implementation of StreamLoader that loads
* "content:" URIs
@@ -68,7 +64,7 @@
protected boolean setupStreamAndSendStatus() {
Uri uri = Uri.parse(mUrl);
if (uri == null) {
- mHandler.error(
+ mLoadListener.error(
EventHandler.FILE_NOT_FOUND_ERROR,
mContext.getString(
com.android.internal.R.string.httpErrorBadUrl) +
@@ -78,18 +74,14 @@
try {
mDataStream = mContext.getContentResolver().openInputStream(uri);
- mHandler.status(1, 1, 200, "OK");
+ mLoadListener.status(1, 1, 200, "OK");
} catch (java.io.FileNotFoundException ex) {
- mHandler.error(EventHandler.FILE_NOT_FOUND_ERROR, errString(ex));
- return false;
-
- } catch (java.io.IOException ex) {
- mHandler.error(EventHandler.FILE_ERROR, errString(ex));
+ mLoadListener.error(EventHandler.FILE_NOT_FOUND_ERROR, errString(ex));
return false;
} catch (RuntimeException ex) {
// readExceptionWithFileNotFoundExceptionFromParcel in DatabaseUtils
// can throw a serial of RuntimeException. Catch them all here.
- mHandler.error(EventHandler.FILE_ERROR, errString(ex));
+ mLoadListener.error(EventHandler.FILE_ERROR, errString(ex));
return false;
}
return true;
@@ -103,16 +95,4 @@
// content can change, we don't want WebKit to cache it
headers.setCacheControl("no-store, no-cache");
}
-
- /**
- * Construct a ContentLoader and instruct it to start loading.
- *
- * @param url "content:" url pointing to content to be loaded
- * @param loadListener LoadListener to pass the content to
- */
- public static void requestUrl(String url, LoadListener loadListener) {
- ContentLoader loader = new ContentLoader(url, loadListener);
- loader.load();
- }
-
}
diff --git a/core/java/android/webkit/CookieManager.java b/core/java/android/webkit/CookieManager.java
index fca591f..94bedde 100644
--- a/core/java/android/webkit/CookieManager.java
+++ b/core/java/android/webkit/CookieManager.java
@@ -20,6 +20,8 @@
import android.net.WebAddress;
import android.util.Log;
+import com.android.common.HttpDateTime;
+
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
diff --git a/core/java/android/webkit/DataLoader.java b/core/java/android/webkit/DataLoader.java
index 2a68a5d..235dc5be 100644
--- a/core/java/android/webkit/DataLoader.java
+++ b/core/java/android/webkit/DataLoader.java
@@ -62,10 +62,10 @@
@Override
protected boolean setupStreamAndSendStatus() {
if (mDataStream != null) {
- mHandler.status(1, 1, 200, "OK");
+ mLoadListener.status(1, 1, 200, "OK");
return true;
} else {
- mHandler.error(EventHandler.ERROR,
+ mLoadListener.error(EventHandler.ERROR,
mContext.getString(R.string.httpError));
return false;
}
@@ -74,16 +74,4 @@
@Override
protected void buildHeaders(android.net.http.Headers h) {
}
-
- /**
- * Construct a DataLoader and instruct it to start loading.
- *
- * @param url data: URL string optionally containing a mimetype
- * @param loadListener LoadListener to pass the content to
- */
- public static void requestUrl(String url, LoadListener loadListener) {
- DataLoader loader = new DataLoader(url, loadListener);
- loader.load();
- }
-
}
diff --git a/core/java/android/webkit/FileLoader.java b/core/java/android/webkit/FileLoader.java
index e856cde..e21e9ef 100644
--- a/core/java/android/webkit/FileLoader.java
+++ b/core/java/android/webkit/FileLoader.java
@@ -18,11 +18,9 @@
import com.android.internal.R;
-import android.content.Context;
import android.content.res.AssetManager;
import android.net.http.EventHandler;
import android.net.http.Headers;
-import android.os.Environment;
import android.util.Log;
import android.util.TypedValue;
@@ -111,7 +109,7 @@
// "<package>.R$drawable"
if (mPath == null || mPath.length() == 0) {
Log.e(LOGTAG, "Need a path to resolve the res file");
- mHandler.error(EventHandler.FILE_ERROR, mContext
+ mLoadListener.error(EventHandler.FILE_ERROR, mContext
.getString(R.string.httpErrorFileNotFound));
return false;
@@ -120,7 +118,7 @@
int dot = mPath.indexOf('.', slash);
if (slash == -1 || dot == -1) {
Log.e(LOGTAG, "Incorrect res path: " + mPath);
- mHandler.error(EventHandler.FILE_ERROR, mContext
+ mLoadListener.error(EventHandler.FILE_ERROR, mContext
.getString(R.string.httpErrorFileNotFound));
return false;
}
@@ -157,13 +155,13 @@
errorMsg = "Caught IllegalAccessException: " + e;
}
if (errorMsg != null) {
- mHandler.error(EventHandler.FILE_ERROR, mContext
+ mLoadListener.error(EventHandler.FILE_ERROR, mContext
.getString(R.string.httpErrorFileNotFound));
return false;
}
} else {
if (!mAllowFileAccess) {
- mHandler.error(EventHandler.FILE_ERROR,
+ mLoadListener.error(EventHandler.FILE_ERROR,
mContext.getString(R.string.httpErrorFileNotFound));
return false;
}
@@ -171,14 +169,14 @@
mDataStream = new FileInputStream(mPath);
mContentLength = (new File(mPath)).length();
}
- mHandler.status(1, 1, 200, "OK");
+ mLoadListener.status(1, 1, 200, "OK");
} catch (java.io.FileNotFoundException ex) {
- mHandler.error(EventHandler.FILE_NOT_FOUND_ERROR, errString(ex));
+ mLoadListener.error(EventHandler.FILE_NOT_FOUND_ERROR, errString(ex));
return false;
} catch (java.io.IOException ex) {
- mHandler.error(EventHandler.FILE_ERROR, errString(ex));
+ mLoadListener.error(EventHandler.FILE_ERROR, errString(ex));
return false;
}
return true;
@@ -188,22 +186,4 @@
protected void buildHeaders(Headers headers) {
// do nothing.
}
-
-
- /**
- * Construct a FileLoader and instruct it to start loading.
- *
- * @param url Full file url pointing to content to be loaded
- * @param loadListener LoadListener to pass the content to
- * @param asset true if url points to an asset.
- * @param allowFileAccess true if this FileLoader can load files from the
- * file system.
- */
- public static void requestUrl(String url, LoadListener loadListener,
- int type, boolean allowFileAccess) {
- FileLoader loader = new FileLoader(url, loadListener, type,
- allowFileAccess);
- loader.load();
- }
-
}
diff --git a/core/java/android/webkit/FrameLoader.java b/core/java/android/webkit/FrameLoader.java
index 58eca38..b13c405 100644
--- a/core/java/android/webkit/FrameLoader.java
+++ b/core/java/android/webkit/FrameLoader.java
@@ -141,24 +141,29 @@
return true;
}
if (URLUtil.isAssetUrl(url)) {
- FileLoader.requestUrl(url, loadListener, FileLoader.TYPE_ASSET,
- true);
+ // load asset in a separate thread as it involves IO
+ new FileLoader(url, loadListener, FileLoader.TYPE_ASSET, true)
+ .enqueue();
return true;
} else if (URLUtil.isResourceUrl(url)) {
- FileLoader.requestUrl(url, loadListener, FileLoader.TYPE_RES,
- true);
+ // load resource in a separate thread as it involves IO
+ new FileLoader(url, loadListener, FileLoader.TYPE_RES, true)
+ .enqueue();
return true;
} else if (URLUtil.isFileUrl(url)) {
- FileLoader.requestUrl(url, loadListener, FileLoader.TYPE_FILE,
- settings.getAllowFileAccess());
+ // load file in a separate thread as it involves IO
+ new FileLoader(url, loadListener, FileLoader.TYPE_FILE, settings
+ .getAllowFileAccess()).enqueue();
return true;
} else if (URLUtil.isContentUrl(url)) {
// Send the raw url to the ContentLoader because it will do a
- // permission check and the url has to match..
- ContentLoader.requestUrl(loadListener.url(), loadListener);
+ // permission check and the url has to match.
+ // load content in a separate thread as it involves IO
+ new ContentLoader(loadListener.url(), loadListener).enqueue();
return true;
} else if (URLUtil.isDataUrl(url)) {
- DataLoader.requestUrl(url, loadListener);
+ // load data in the current thread to reduce the latency
+ new DataLoader(url, loadListener).load();
return true;
} else if (URLUtil.isAboutUrl(url)) {
loadListener.data(mAboutBlank.getBytes(), mAboutBlank.length());
diff --git a/core/java/android/webkit/HttpDateTime.java b/core/java/android/webkit/HttpDateTime.java
deleted file mode 100644
index 042953c..0000000
--- a/core/java/android/webkit/HttpDateTime.java
+++ /dev/null
@@ -1,224 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.webkit;
-
-import android.text.format.Time;
-
-import java.util.Calendar;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-
-/** {@hide} */
-public final class HttpDateTime {
-
- /*
- * Regular expression for parsing HTTP-date.
- *
- * Wdy, DD Mon YYYY HH:MM:SS GMT
- * RFC 822, updated by RFC 1123
- *
- * Weekday, DD-Mon-YY HH:MM:SS GMT
- * RFC 850, obsoleted by RFC 1036
- *
- * Wdy Mon DD HH:MM:SS YYYY
- * ANSI C's asctime() format
- *
- * with following variations
- *
- * Wdy, DD-Mon-YYYY HH:MM:SS GMT
- * Wdy, (SP)D Mon YYYY HH:MM:SS GMT
- * Wdy,DD Mon YYYY HH:MM:SS GMT
- * Wdy, DD-Mon-YY HH:MM:SS GMT
- * Wdy, DD Mon YYYY HH:MM:SS -HHMM
- * Wdy, DD Mon YYYY HH:MM:SS
- * Wdy Mon (SP)D HH:MM:SS YYYY
- * Wdy Mon DD HH:MM:SS YYYY GMT
- *
- * HH can be H if the first digit is zero.
- *
- * Mon can be the full name of the month.
- */
- private static final String HTTP_DATE_RFC_REGEXP =
- "([0-9]{1,2})[- ]([A-Za-z]{3,9})[- ]([0-9]{2,4})[ ]"
- + "([0-9]{1,2}:[0-9][0-9]:[0-9][0-9])";
-
- private static final String HTTP_DATE_ANSIC_REGEXP =
- "[ ]([A-Za-z]{3,9})[ ]+([0-9]{1,2})[ ]"
- + "([0-9]{1,2}:[0-9][0-9]:[0-9][0-9])[ ]([0-9]{2,4})";
-
- /**
- * The compiled version of the HTTP-date regular expressions.
- */
- private static final Pattern HTTP_DATE_RFC_PATTERN =
- Pattern.compile(HTTP_DATE_RFC_REGEXP);
- private static final Pattern HTTP_DATE_ANSIC_PATTERN =
- Pattern.compile(HTTP_DATE_ANSIC_REGEXP);
-
- private static class TimeOfDay {
- TimeOfDay(int h, int m, int s) {
- this.hour = h;
- this.minute = m;
- this.second = s;
- }
-
- int hour;
- int minute;
- int second;
- }
-
- public static Long parse(String timeString)
- throws IllegalArgumentException {
-
- int date = 1;
- int month = Calendar.JANUARY;
- int year = 1970;
- TimeOfDay timeOfDay;
-
- Matcher rfcMatcher = HTTP_DATE_RFC_PATTERN.matcher(timeString);
- if (rfcMatcher.find()) {
- date = getDate(rfcMatcher.group(1));
- month = getMonth(rfcMatcher.group(2));
- year = getYear(rfcMatcher.group(3));
- timeOfDay = getTime(rfcMatcher.group(4));
- } else {
- Matcher ansicMatcher = HTTP_DATE_ANSIC_PATTERN.matcher(timeString);
- if (ansicMatcher.find()) {
- month = getMonth(ansicMatcher.group(1));
- date = getDate(ansicMatcher.group(2));
- timeOfDay = getTime(ansicMatcher.group(3));
- year = getYear(ansicMatcher.group(4));
- } else {
- throw new IllegalArgumentException();
- }
- }
-
- // FIXME: Y2038 BUG!
- if (year >= 2038) {
- year = 2038;
- month = Calendar.JANUARY;
- date = 1;
- }
-
- Time time = new Time(Time.TIMEZONE_UTC);
- time.set(timeOfDay.second, timeOfDay.minute, timeOfDay.hour, date,
- month, year);
- return time.toMillis(false /* use isDst */);
- }
-
- private static int getDate(String dateString) {
- if (dateString.length() == 2) {
- return (dateString.charAt(0) - '0') * 10
- + (dateString.charAt(1) - '0');
- } else {
- return (dateString.charAt(0) - '0');
- }
- }
-
- /*
- * jan = 9 + 0 + 13 = 22
- * feb = 5 + 4 + 1 = 10
- * mar = 12 + 0 + 17 = 29
- * apr = 0 + 15 + 17 = 32
- * may = 12 + 0 + 24 = 36
- * jun = 9 + 20 + 13 = 42
- * jul = 9 + 20 + 11 = 40
- * aug = 0 + 20 + 6 = 26
- * sep = 18 + 4 + 15 = 37
- * oct = 14 + 2 + 19 = 35
- * nov = 13 + 14 + 21 = 48
- * dec = 3 + 4 + 2 = 9
- */
- private static int getMonth(String monthString) {
- int hash = Character.toLowerCase(monthString.charAt(0)) +
- Character.toLowerCase(monthString.charAt(1)) +
- Character.toLowerCase(monthString.charAt(2)) - 3 * 'a';
- switch (hash) {
- case 22:
- return Calendar.JANUARY;
- case 10:
- return Calendar.FEBRUARY;
- case 29:
- return Calendar.MARCH;
- case 32:
- return Calendar.APRIL;
- case 36:
- return Calendar.MAY;
- case 42:
- return Calendar.JUNE;
- case 40:
- return Calendar.JULY;
- case 26:
- return Calendar.AUGUST;
- case 37:
- return Calendar.SEPTEMBER;
- case 35:
- return Calendar.OCTOBER;
- case 48:
- return Calendar.NOVEMBER;
- case 9:
- return Calendar.DECEMBER;
- default:
- throw new IllegalArgumentException();
- }
- }
-
- private static int getYear(String yearString) {
- if (yearString.length() == 2) {
- int year = (yearString.charAt(0) - '0') * 10
- + (yearString.charAt(1) - '0');
- if (year >= 70) {
- return year + 1900;
- } else {
- return year + 2000;
- }
- } else if (yearString.length() == 3) {
- // According to RFC 2822, three digit years should be added to 1900.
- int year = (yearString.charAt(0) - '0') * 100
- + (yearString.charAt(1) - '0') * 10
- + (yearString.charAt(2) - '0');
- return year + 1900;
- } else if (yearString.length() == 4) {
- return (yearString.charAt(0) - '0') * 1000
- + (yearString.charAt(1) - '0') * 100
- + (yearString.charAt(2) - '0') * 10
- + (yearString.charAt(3) - '0');
- } else {
- return 1970;
- }
- }
-
- private static TimeOfDay getTime(String timeString) {
- // HH might be H
- int i = 0;
- int hour = timeString.charAt(i++) - '0';
- if (timeString.charAt(i) != ':')
- hour = hour * 10 + (timeString.charAt(i++) - '0');
- // Skip ':'
- i++;
-
- int minute = (timeString.charAt(i++) - '0') * 10
- + (timeString.charAt(i++) - '0');
- // Skip ':'
- i++;
-
- int second = (timeString.charAt(i++) - '0') * 10
- + (timeString.charAt(i++) - '0');
-
- return new TimeOfDay(hour, minute, second);
- }
-}
diff --git a/core/java/android/webkit/StreamLoader.java b/core/java/android/webkit/StreamLoader.java
index ce26268..4c32997 100644
--- a/core/java/android/webkit/StreamLoader.java
+++ b/core/java/android/webkit/StreamLoader.java
@@ -20,12 +20,13 @@
import android.net.http.EventHandler;
import android.net.http.Headers;
import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
import android.os.Message;
import java.io.IOException;
import java.io.InputStream;
-
/**
* This abstract class is used for all content loaders that rely on streaming
* content into the rendering engine loading framework.
@@ -44,9 +45,7 @@
* that indicates the content should not be cached.
*
*/
-abstract class StreamLoader extends Handler {
-
- public static final String NO_STORE = "no-store";
+abstract class StreamLoader implements Handler.Callback {
private static final int MSG_STATUS = 100; // Send status to loader
private static final int MSG_HEADERS = 101; // Send headers to loader
@@ -54,11 +53,19 @@
private static final int MSG_END = 103; // Send endData to loader
protected final Context mContext;
- protected final LoadListener mHandler; // loader class
+ protected final LoadListener mLoadListener; // loader class
protected InputStream mDataStream; // stream to read data from
protected long mContentLength; // content length of data
private byte [] mData; // buffer to pass data to loader with.
+ // Handler which will be initialized in the thread where load() is called.
+ private Handler mHandler;
+
+ // Handler which will be used to load StreamLoader in a separate thread
+ private static StreamQueueHandler sStreamQueueHandler;
+
+ private static final Object sStreamQueueLock = new Object();
+
/**
* Constructor. Although this class calls the LoadListener, it only calls
* the EventHandler Interface methods. LoadListener concrete class is used
@@ -67,13 +74,13 @@
* @param loadlistener The LoadListener to call with the data.
*/
StreamLoader(LoadListener loadlistener) {
- mHandler = loadlistener;
+ mLoadListener = loadlistener;
mContext = loadlistener.getContext();
}
/**
* This method is called when the derived class should setup mDataStream,
- * and call mHandler.status() to indicate that the load can occur. If it
+ * and call mLoadListener.status() to indicate that the load can occur. If it
* fails to setup, it should still call status() with the error code.
*
* @return true if stream was successfully setup
@@ -89,15 +96,40 @@
*/
abstract protected void buildHeaders(Headers headers);
+ /**
+ * Calling this method to load this StreamLoader in a separate
+ * "StreamLoadingThread".
+ */
+ final void enqueue() {
+ synchronized (sStreamQueueLock) {
+ if (sStreamQueueHandler == null) {
+ HandlerThread thread = new HandlerThread(
+ StreamQueueHandler.THREAD_NAME,
+ android.os.Process.THREAD_PRIORITY_DEFAULT +
+ android.os.Process.THREAD_PRIORITY_LESS_FAVORABLE);
+ thread.start();
+ sStreamQueueHandler = new StreamQueueHandler(thread.getLooper());
+ }
+ }
+
+ sStreamQueueHandler.obtainMessage(StreamQueueHandler.MSG_ADD_LOADER,
+ this).sendToTarget();
+ }
/**
* Calling this method starts the load of the content for this StreamLoader.
- * This method simply posts a message to send the status and returns
- * immediately.
+ * This method simply creates a Handler in the current thread and posts a
+ * message to send the status and returns immediately.
*/
- public void load() {
- if (!mHandler.isSynchronous()) {
- sendMessage(obtainMessage(MSG_STATUS));
+ final void load() {
+ synchronized (this) {
+ if (mHandler == null) {
+ mHandler = new Handler(this);
+ }
+ }
+
+ if (!mLoadListener.isSynchronous()) {
+ mHandler.sendEmptyMessage(MSG_STATUS);
} else {
// Load the stream synchronously.
if (setupStreamAndSendStatus()) {
@@ -105,23 +137,20 @@
// to pass data to the loader
mData = new byte[8192];
sendHeaders();
- while (!sendData() && !mHandler.cancelled());
+ while (!sendData() && !mLoadListener.cancelled());
closeStreamAndSendEndData();
- mHandler.loadSynchronousMessages();
+ mLoadListener.loadSynchronousMessages();
}
}
}
- /* (non-Javadoc)
- * @see android.os.Handler#handleMessage(android.os.Message)
- */
- public void handleMessage(Message msg) {
- if (DebugFlags.STREAM_LOADER && mHandler.isSynchronous()) {
+ public boolean handleMessage(Message msg) {
+ if (mLoadListener.isSynchronous()) {
throw new AssertionError();
}
- if (mHandler.cancelled()) {
+ if (mLoadListener.cancelled()) {
closeStreamAndSendEndData();
- return;
+ return true;
}
switch(msg.what) {
case MSG_STATUS:
@@ -129,27 +158,27 @@
// We were able to open the stream, create the array
// to pass data to the loader
mData = new byte[8192];
- sendMessage(obtainMessage(MSG_HEADERS));
+ mHandler.sendEmptyMessage(MSG_HEADERS);
}
break;
case MSG_HEADERS:
sendHeaders();
- sendMessage(obtainMessage(MSG_DATA));
+ mHandler.sendEmptyMessage(MSG_DATA);
break;
case MSG_DATA:
if (sendData()) {
- sendMessage(obtainMessage(MSG_END));
+ mHandler.sendEmptyMessage(MSG_END);
} else {
- sendMessage(obtainMessage(MSG_DATA));
+ mHandler.sendEmptyMessage(MSG_DATA);
}
break;
case MSG_END:
closeStreamAndSendEndData();
break;
default:
- super.handleMessage(msg);
- break;
+ return false;
}
+ return true;
}
/**
@@ -161,7 +190,7 @@
headers.setContentLength(mContentLength);
}
buildHeaders(headers);
- mHandler.headers(headers);
+ mLoadListener.headers(headers);
}
/**
@@ -176,12 +205,11 @@
try {
int amount = mDataStream.read(mData);
if (amount > 0) {
- mHandler.data(mData, amount);
+ mLoadListener.data(mData, amount);
return false;
}
} catch (IOException ex) {
- mHandler.error(EventHandler.FILE_ERROR,
- ex.getMessage());
+ mLoadListener.error(EventHandler.FILE_ERROR, ex.getMessage());
}
}
return true;
@@ -198,7 +226,24 @@
// ignore.
}
}
- mHandler.endData();
+ mLoadListener.endData();
}
+ private static class StreamQueueHandler extends Handler {
+ private static final String THREAD_NAME = "StreamLoadingThread";
+
+ private static final int MSG_ADD_LOADER = 101;
+
+ StreamQueueHandler(Looper looper) {
+ super(looper);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ if (msg.what == MSG_ADD_LOADER) {
+ StreamLoader loader = (StreamLoader) msg.obj;
+ loader.load();
+ }
+ }
+ }
}
diff --git a/core/java/android/webkit/WebChromeClient.java b/core/java/android/webkit/WebChromeClient.java
index f40b55c..1d5aac7 100644
--- a/core/java/android/webkit/WebChromeClient.java
+++ b/core/java/android/webkit/WebChromeClient.java
@@ -262,8 +262,24 @@
* @param message The error message to report.
* @param lineNumber The line number of the error.
* @param sourceID The name of the source file that caused the error.
+ * @deprecated Use {@link #onConsoleMessage(ConsoleMessage) onConsoleMessage(ConsoleMessage)}
+ * instead.
*/
- public void onConsoleMessage(String message, int lineNumber, String sourceID) {}
+ @Deprecated
+ public void onConsoleMessage(String message, int lineNumber, String sourceID) { }
+
+ /**
+ * Report a JavaScript console message to the host application. The ChromeClient
+ * should override this to process the log message as they see fit.
+ * @param consoleMessage Object containing details of the console message.
+ * @return true if the message is handled by the client.
+ */
+ public boolean onConsoleMessage(ConsoleMessage consoleMessage) {
+ // Call the old version of this function for backwards compatability.
+ onConsoleMessage(consoleMessage.message(), consoleMessage.lineNumber(),
+ consoleMessage.sourceId());
+ return false;
+ }
/**
* When not playing, video elements are represented by a 'poster' image. The
diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java
index 6e45e39..9c91919 100644
--- a/core/java/android/webkit/WebViewCore.java
+++ b/core/java/android/webkit/WebViewCore.java
@@ -260,9 +260,12 @@
* @param message The message to add
* @param lineNumber the line on which the error occurred
* @param sourceID the filename of the source that caused the error.
+ * @param msgLevel the log level of this message. This is a value casted to int
+ * from WebCore::MessageLevel in WebCore/page/Console.h.
*/
- protected void addMessageToConsole(String message, int lineNumber, String sourceID) {
- mCallbackProxy.addMessageToConsole(message, lineNumber, sourceID);
+ protected void addMessageToConsole(String message, int lineNumber, String sourceID,
+ int msgLevel) {
+ mCallbackProxy.addMessageToConsole(message, lineNumber, sourceID, msgLevel);
}
/**
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 1cc1c26..66a7631 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -1892,9 +1892,10 @@
// Check if we have moved far enough that it looks more like a
// scroll than a tap
final int distance = Math.abs(deltaY);
- if (distance > mTouchSlop) {
+ final boolean overscroll = mScrollY != 0;
+ if (overscroll || distance > mTouchSlop) {
createScrollingCache();
- mTouchMode = TOUCH_MODE_SCROLL;
+ mTouchMode = overscroll ? TOUCH_MODE_OVERSCROLL : TOUCH_MODE_SCROLL;
mMotionCorrection = deltaY;
final Handler handler = getHandler();
// Handler should not be null unless the AbsListView is not attached to a
@@ -2097,7 +2098,6 @@
// Check to see if we are back in
View motionView = this.getChildAt(mMotionPosition - mFirstPosition);
if (motionView != null) {
- int topOffset = motionView.getTop() - mMotionViewNewTop;
mTouchMode = TOUCH_MODE_SCROLL;
// We did not scroll the full amount. Treat this essentially like the
diff --git a/core/java/android/widget/OverScroller.java b/core/java/android/widget/OverScroller.java
index 44d415e..b11caa1 100644
--- a/core/java/android/widget/OverScroller.java
+++ b/core/java/android/widget/OverScroller.java
@@ -125,8 +125,8 @@
final float durationSecs = (OVERFLING_DURATION / 1000.f);
int dx = (int)(xvel * durationSecs) / 8;
int dy = (int)(yvel * durationSecs) / 8;
- scroller.startScroll(startx, starty, dx, dy, OVERFLING_DURATION);
mCurrScroller.abortAnimation();
+ scroller.startScroll(startx, starty, dx, dy, OVERFLING_DURATION);
mCurrScroller = scroller;
mScrollMode = MODE_OVERFLING;
}
@@ -164,8 +164,8 @@
}
if (xoff != 0 || yoff != 0) {
- scroller.startScroll(startX, startY, xoff, yoff, SPRINGBACK_DURATION);
mCurrScroller.abortAnimation();
+ scroller.startScroll(startX, startY, xoff, yoff, SPRINGBACK_DURATION);
mCurrScroller = scroller;
mScrollMode = MODE_SPRINGBACK;
return true;
diff --git a/core/java/com/android/internal/app/TetherActivity.java b/core/java/com/android/internal/app/TetherActivity.java
new file mode 100644
index 0000000..2b93dbc
--- /dev/null
+++ b/core/java/com/android/internal/app/TetherActivity.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 2007 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.app;
+
+import android.app.AlertDialog;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.ConnectivityManager;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IMountService;
+import android.os.Message;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.widget.Toast;
+import android.util.Log;
+
+/**
+ * This activity is shown to the user for him/her to connect/disconnect a Tether
+ * connection. It will display notification when a suitable connection is made
+ * to allow the tether to be setup. A second notification will be show when a
+ * tether is active, allowing the user to manage tethered connections.
+ */
+public class TetherActivity extends AlertActivity implements
+ DialogInterface.OnClickListener {
+
+ private static final int POSITIVE_BUTTON = AlertDialog.BUTTON1;
+
+ /* Used to detect when the USB cable is unplugged, so we can call finish() */
+ private BroadcastReceiver mTetherReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (intent.getAction() == ConnectivityManager.ACTION_TETHER_STATE_CHANGED) {
+ handleTetherStateChanged(intent);
+ }
+ }
+ };
+
+ private boolean mWantTethering;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ // determine if we advertise tethering or untethering
+ ConnectivityManager cm =
+ (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
+ if (cm.getTetheredIfaces().length > 0) {
+ mWantTethering = false;
+ } else if (cm.getTetherableIfaces().length > 0) {
+ mWantTethering = true;
+ } else {
+ finish();
+ return;
+ }
+
+ // Set up the "dialog"
+ if (mWantTethering == true) {
+ mAlertParams.mIconId = com.android.internal.R.drawable.ic_dialog_usb;
+ mAlertParams.mTitle = getString(com.android.internal.R.string.tether_title);
+ mAlertParams.mMessage = getString(com.android.internal.R.string.tether_message);
+ mAlertParams.mPositiveButtonText =
+ getString(com.android.internal.R.string.tether_button);
+ mAlertParams.mPositiveButtonListener = this;
+ mAlertParams.mNegativeButtonText =
+ getString(com.android.internal.R.string.tether_button_cancel);
+ mAlertParams.mNegativeButtonListener = this;
+ } else {
+ mAlertParams.mIconId = com.android.internal.R.drawable.ic_dialog_usb;
+ mAlertParams.mTitle = getString(com.android.internal.R.string.tether_stop_title);
+ mAlertParams.mMessage = getString(com.android.internal.R.string.tether_stop_message);
+ mAlertParams.mPositiveButtonText =
+ getString(com.android.internal.R.string.tether_stop_button);
+ mAlertParams.mPositiveButtonListener = this;
+ mAlertParams.mNegativeButtonText =
+ getString(com.android.internal.R.string.tether_stop_button_cancel);
+ mAlertParams.mNegativeButtonListener = this;
+ }
+ setupAlert();
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+
+ registerReceiver(mTetherReceiver, new IntentFilter(
+ ConnectivityManager.ACTION_TETHER_STATE_CHANGED));
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+
+ unregisterReceiver(mTetherReceiver);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void onClick(DialogInterface dialog, int which) {
+
+ if (which == POSITIVE_BUTTON) {
+ ConnectivityManager connManager =
+ (ConnectivityManager)getSystemService(Context.CONNECTIVITY_SERVICE);
+ // start/stop tethering
+ if (mWantTethering) {
+ if (!connManager.tether("ppp0")) {
+ showTetheringError();
+ }
+ } else {
+ if (!connManager.untether("ppp0")) {
+ showUnTetheringError();
+ }
+ }
+ }
+ // No matter what, finish the activity
+ finish();
+ }
+
+ private void handleTetherStateChanged(Intent intent) {
+ finish();
+ }
+
+ private void showTetheringError() {
+ Toast.makeText(this, com.android.internal.R.string.tether_error_message,
+ Toast.LENGTH_LONG).show();
+ }
+
+ private void showUnTetheringError() {
+ Toast.makeText(this, com.android.internal.R.string.tether_stop_error_message,
+ Toast.LENGTH_LONG).show();
+ }
+
+}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 665088a..1406b66 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -1239,6 +1239,10 @@
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
+ <activity android:name="com.android.internal.app.TetherActivity"
+ android:theme="@style/Theme.Dialog.Alert"
+ android:excludeFromRecents="true">
+ </activity>
<activity android:name="com.android.internal.app.UsbStorageActivity"
android:excludeFromRecents="true">
</activity>
diff --git a/core/res/res/drawable-hdpi/search_source_selector_indicator.png b/core/res/res/drawable-hdpi/search_source_selector_indicator.png
deleted file mode 100644
index b93a0c0..0000000
--- a/core/res/res/drawable-hdpi/search_source_selector_indicator.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_tether_active.png b/core/res/res/drawable-hdpi/stat_sys_tether_active.png
new file mode 100755
index 0000000..4c14c07
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_sys_tether_active.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/stat_sys_tether_usb.png b/core/res/res/drawable-hdpi/stat_sys_tether_usb.png
new file mode 100755
index 0000000..4c14c07
--- /dev/null
+++ b/core/res/res/drawable-hdpi/stat_sys_tether_usb.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/usb_android.png b/core/res/res/drawable-hdpi/usb_android.png
index 8153ec4..f6f899a 100644
--- a/core/res/res/drawable-hdpi/usb_android.png
+++ b/core/res/res/drawable-hdpi/usb_android.png
Binary files differ
diff --git a/core/res/res/drawable-hdpi/usb_android_connected.png b/core/res/res/drawable-hdpi/usb_android_connected.png
index 6449b7c..583ca00 100644
--- a/core/res/res/drawable-hdpi/usb_android_connected.png
+++ b/core/res/res/drawable-hdpi/usb_android_connected.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/search_source_selector_indicator.png b/core/res/res/drawable-mdpi/search_source_selector_indicator.png
deleted file mode 100644
index 26bf18a..0000000
--- a/core/res/res/drawable-mdpi/search_source_selector_indicator.png
+++ /dev/null
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_tether_active.png b/core/res/res/drawable-mdpi/stat_sys_tether_active.png
new file mode 100644
index 0000000..2d0da4c
--- /dev/null
+++ b/core/res/res/drawable-mdpi/stat_sys_tether_active.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/stat_sys_tether_usb.png b/core/res/res/drawable-mdpi/stat_sys_tether_usb.png
new file mode 100644
index 0000000..2d0da4c
--- /dev/null
+++ b/core/res/res/drawable-mdpi/stat_sys_tether_usb.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/usb_android.png b/core/res/res/drawable-mdpi/usb_android.png
new file mode 100644
index 0000000..df1afbb
--- /dev/null
+++ b/core/res/res/drawable-mdpi/usb_android.png
Binary files differ
diff --git a/core/res/res/drawable-mdpi/usb_android_connected.png b/core/res/res/drawable-mdpi/usb_android_connected.png
new file mode 100644
index 0000000..fca77a7
--- /dev/null
+++ b/core/res/res/drawable-mdpi/usb_android_connected.png
Binary files differ
diff --git a/core/res/res/drawable/search_source_selector_background.xml b/core/res/res/drawable/search_source_selector_background.xml
deleted file mode 100644
index fcacd89..0000000
--- a/core/res/res/drawable/search_source_selector_background.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
-
- <!-- TODO: Need focused and pressed backgrounds -->
-
-</selector>
diff --git a/core/res/res/layout/search_bar.xml b/core/res/res/layout/search_bar.xml
index 12285fd..cf246ba 100644
--- a/core/res/res/layout/search_bar.xml
+++ b/core/res/res/layout/search_bar.xml
@@ -55,11 +55,14 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
-
- <include android:id="@+id/search_source_selector"
- layout="@layout/search_source_selector"
+
+ <ImageView
+ android:id="@+id/search_app_icon"
+ android:layout_height="36dip"
+ android:layout_width="36dip"
android:layout_marginRight="7dip"
- android:layout_gravity="center_vertical" />
+ android:layout_gravity="center_vertical"
+ />
<view class="android.app.SearchDialog$SearchAutoComplete"
android:id="@+id/search_src_text"
diff --git a/core/res/res/layout/search_source_selector.xml b/core/res/res/layout/search_source_selector.xml
deleted file mode 100644
index c69dfc0..0000000
--- a/core/res/res/layout/search_source_selector.xml
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- 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.
--->
-
-<FrameLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="48dip"
- android:layout_height="match_parent"
- android:foreground="@drawable/search_source_selector_indicator"
- android:foregroundGravity="bottom|right"
- >
-
- <ImageButton
- android:id="@+id/search_source_selector_icon"
- android:background="@drawable/search_source_selector_background"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:scaleType="centerInside"
- android:focusable="true"
- android:clickable="true"
- />
-
-</FrameLayout>
diff --git a/core/res/res/values-cs/donottranslate-cldr.xml b/core/res/res/values-cs/donottranslate-cldr.xml
index e90e53c..a026734 100644
--- a/core/res/res/values-cs/donottranslate-cldr.xml
+++ b/core/res/res/values-cs/donottranslate-cldr.xml
@@ -144,6 +144,6 @@
<string name="same_month_mdy1_mdy2">%3$s.-%8$s. %2$s %9$s</string>
<string name="same_year_wday1_mdy1_wday2_mdy2">%1$s %3$s. %2$s - %6$s %8$s. %7$s %9$s</string>
<string name="short_format_month">%b</string>
- <string name="full_wday_month_day_no_year">EEEE MMMM d</string>
+ <string name="full_wday_month_day_no_year">EEEE, d. MMMM</string>
<string name="abbrev_wday_month_day_year">E d. MMMM yyyy</string>
</resources>
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 7728c50..54781e3 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -923,6 +923,19 @@
<attr name="name" />
</declare-styleable>
+ <!-- Private tag to declare the original package name that this package is
+ based on. Only used for packages installed in the system image. If
+ given, and different than the actual package name, and the given
+ original package was previously installed on the device but the new
+ one was not, then the data for the old one will be renamed to be
+ for the new package.
+
+ <p>This appears as a child tag of the root
+ {@link #AndroidManifest manifest} tag. -->
+ <declare-styleable name="AndroidManifestOriginalPackage" parent="AndroidManifest">
+ <attr name="name" />
+ </declare-styleable>
+
<!-- The <code>provider</code> tag declares a
{@link android.content.ContentProvider} class that is available
as part of the package's application components, supplying structured
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 2b8ddc4..46d6352 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -237,4 +237,8 @@
<!-- Component name of the service providing geocoder API support. -->
<string name="config_geocodeProvider">@null</string>
+
+ <!-- Flag indicating whether headset events are used by kernel to indicate
+ TTY mode changes. -->
+ <bool name="tty_mode_uses_headset_events">false</bool>
</resources>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 30d0da7..d1bfc68 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1078,7 +1078,13 @@
<string name="permlab_changeNetworkState">change network connectivity</string>
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permdesc_changeNetworkState">Allows an application to change
- the state network connectivity.</string>
+ the state of network connectivity.</string>
+
+ <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+ <string name="permlab_changeTetherState">change tethered connectivity</string>
+ <!-- Description of an application permission, listed so the user can choose whether they want to allow the applicaiton to do this. -->
+ <string name="permdesc_changeTetherState">Allows an application to change
+ the state of tethered network connectivity.</string>
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permlab_changeBackgroundDataSetting">change background data usage setting</string>
@@ -2200,4 +2206,40 @@
Used by AccessibilityService to announce the purpose of the view.
-->
<string name="description_star">favorite</string>
+
+
+ <!-- Strings for Tethering dialogs -->
+ <!-- This is the label for the activity, and should never be visible to the user. -->
+ <!-- See TETHERING. TETHERING_DIALOG: After the user selects the notification, a dialog is shown asking if he wants to Tether. This is the title. -->
+ <string name="tether_title">USB tethering available</string>
+ <!-- See TETHER. This is the message. -->
+ <string name="tether_message">Select \"Tether\" if you want to share your phone\'s data connection with your computer.</string>
+ <!-- See TETHER. This is the button text to Tether the computer with the phone. -->
+ <string name="tether_button">Tether</string>
+ <!-- See TETHER. This is the button text to ignore the plugging in of the phone.. -->
+ <string name="tether_button_cancel">Cancel</string>
+ <!-- See TETHER. If there was an error mounting, this is the text. -->
+ <string name="tether_error_message">There is a problem tethering.</string>
+ <!-- TETHER: When the user connects the phone to a computer, we show a notification asking if he wants to share his cellular network connection. This is the title -->
+ <string name="tether_available_notification_title">USB tethering available</string>
+ <!-- See USB_STORAGE. This is the message. -->
+ <string name="tether_available_notification_message">Select to tether your computer to your phone.</string>
+ <!-- TETHER_STOP: While TETHER is enabled, we show a notification dialog asking if he wants to stop. This is the title -->
+ <string name="tether_stop_notification_title">Untether</string>
+ <!-- See TETHER. This is the message. -->
+ <string name="tether_stop_notification_message">Select to untether your computer.</string>
+
+ <!-- TETHER stop dialog strings -->
+ <!-- This is the label for the activity, and should never be visible to the user. -->
+ <!-- See TETHER_STOP. TETHER_STOP_DIALOG: After the user selects the notification, a dialog is shown asking if he wants to stop tethering. This is the title. -->
+ <string name="tether_stop_title">Disconnect tethering</string>
+ <!-- See TETHER_STOP. This is the message. -->
+ <string name="tether_stop_message">You have been sharing your phone\'s cellular data connection with your computer. Select \"Disconnect\" to disconnect USB tethering.</string>
+ <!-- See TETHER_STOP. This is the button text to disconnect tethering. -->
+ <string name="tether_stop_button">Disconnect</string>
+ <!-- See TETHER_STOP. This is the button text to cancel disconnecting the tether. -->
+ <string name="tether_stop_button_cancel">Cancel</string>
+ <!-- See TETHER_STOP_DIALOG. If there was an error disconnect, this is the text. -->
+ <string name="tether_stop_error_message">We\'ve encountered a problem turning off Tethering. Please try again.</string>
+
</resources>
diff --git a/docs/html/guide/topics/ui/themes.jd b/docs/html/guide/topics/ui/themes.jd
index 03995126..de699f2 100644
--- a/docs/html/guide/topics/ui/themes.jd
+++ b/docs/html/guide/topics/ui/themes.jd
@@ -7,143 +7,328 @@
<div id="qv">
<h2>In this document</h2>
<ol>
- <li><a href="#styles">Styles</a></li>
- <li><a href="#themes">Themes</a>
+ <li><a href="#DefiningStyles">Defining Styles</a>
<ol>
- <li><a href="#inTheManifest">Set the theme in the manifest</a></li>
- <li><a href="#fromTheApp">Set the theme from the application</a></li>
+ <li><a href="#Inheritance">Inheritance</a></li>
+ <li><a href="#Properties">Style Properties</a></li>
</ol>
</li>
+ <li><a href="#ApplyingStyles">Applying Styles and Themes to the UI</a>
+ <ol>
+ <li><a href="#ApplyAStyle">Apply a style to a View</a></li>
+ <li><a href="#ApplyATheme">Apply a theme to an Activity or application</a></li>
+ </ol>
+ </li>
+ <li><a href="#PlatformStyles">Using Platform Styles and Themes</a></li>
</ol>
<h2>See also</h2>
<ol>
- <li><a href="{@docRoot}guide/topics/resources/available-resources.html#stylesandthemes">Style and Theme Resources</a></li>
+ <li><a href="{@docRoot}guide/topics/resources/available-resources.html#stylesandthemes">Style
+ and Theme Resources</a></li>
+ <li>{@link android.R.style} for Android styles and themes</li>
+ <li>{@link android.R.attr} for all style attributes</li>
</ol>
</div>
</div>
-<p>When designing your application, you can use <em>styles</em> and <em>themes</em> to apply uniform formatting to its various screens and UI elements.
-<ul>
- <li>A style is a set of one or more formatting attributes that you can apply as a unit to single elements in your layout XML file(s). For example, you could define a style that specifies a certain text size and color, then apply it to instances of a certain type of View element.</li>
- <li>A theme is a set of one or more formatting attributes that you can apply as a unit to all activities in an application or just a single activity. For example, you could define a theme that sets specific colors for the window frame and the panel foreground and background, and sets text sizes and colors for menus, then apply it to the activities of your application.</li>
-</ul>
+<p>A <strong>style</strong> is a collection of properties that
+specify the look and format for a {@link android.view.View} or window.
+A style can specify properties such as height, padding, font color, font size,
+background color, and much more. A style is defined in an XML resource that is
+separate from the XML that specifies the layout.</p>
-<p>Styles and themes are resources. Android provides some default style and theme resources that you can use, or you can declare your own custom style and theme resources.</p>
+<p>Styles in Android share a similar philosophy to cascading stylesheets in web
+design—they allow you to separate the design from the
+content.</p>
-<p>To create custom styles and themes:</p>
-<ol>
- <li>Create a file named <code>styles.xml</code> in the your application's <code>res/values</code> directory. Add a root <code><resources></code> node.</li>
- <li>For each style or theme, add a <code><style></code> element with a unique <code>name</code> and, optionally, a <code>parent</code> attribute.
- The name is used for referencing these styles later, and the parent indicates what style resource to inherit from.</li>
- <li>Inside the <code><style></code> element, declare format values in one or more <code><item></code> element(s).
- Each <code><item></code> identifies its style property with a <code>name</code> attribute and defines its style value inside the element.</li>
- <li>You can then reference the custom resources from other XML resources, your manifest or application code.</li>
-</ol>
+<p>For example, by using a style, you can take this layout XML:</p>
+<pre>
+<TextView
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:textColor="#00FF00"
+ android:typeface="monospace"
+ android:text="@string/hello" />
+</pre>
+<p>And turn it into this:</p>
+<pre>
+<TextView
+ style="@style/CodeFont"
+ android:text="@string/hello" />
+</pre>
+
+<p>All of the attributes related to style have been removed from the layout XML and put into a
+style definition called {@code CodeFont}, which is then applied with the <code>style</code>
+attribute. You'll see the definition for this style in the following section.</p>
+
+<p>A <strong>theme</strong> is a style applied to an entire {@link android.app.Activity} or
+application, rather than an individual {@link android.view.View} (as in the example above). When a
+style is applied as a theme, every View in the Activity or application will apply each style
+property that it supports. For example, you can apply the same {@code CodeFont} style
+as a theme for an Activity and then all text inside that Activity will have green monospace
+font.</p>
-<h2 id="styles">Styles</h2>
+<h2 id="DefiningStyles">Defining Styles</h2>
-<p>Here's an example declaration of a style: </p>
+<p>To create a set of styles, save an XML file in the {@code res/values/}
+directory of your project. The name of the XML file is arbitrary, but it must use the
+{@code .xml} extension and be saved in the {@code res/values/} folder.</p>
+
+<p>The root node of the XML file must be {@code <resources>}.</p>
+
+<p>For each style you want to create, add a {@code <style>} element to the file
+with a {@code name} that uniquely identifies the style (this attribute is required).
+Then add an {@code <item>} element for each property of that style, with a
+{@code name} that declares the style property and a value to go with it (this attribute
+is required). The value for the {@code <item>} can
+be a keyword string, a hex color, a reference to another resource type, or other value
+depending on the style property.
+Here's an example file with a single style:</p>
<pre>
-<?xml version="1.0" encoding="utf-8"?>
+<?xml version="1.0" encoding="utf-8"?>
<resources>
- <style name="SpecialText" parent="@style/Text">
- <item name="android:textSize">18sp</item>
- <item name="android:textColor">#008</item>
+ <style name="CodeFont" parent="@android:style/TextAppearance.Medium">
+ <item name="android:layout_width">fill_parent</item>
+ <item name="android:layout_height">wrap_content</item>
+ <item name="android:textColor">#00FF00</item>
+ <item name="android:typeface">monospace</item>
</style>
</resources>
</pre>
-<p>As shown, you can use <code><item></code> elements to set specific formatting values for the style.
-The <code>name</code> attribute in the <code>item</code> can refer to a standard string, a hex color value,
-or a reference to any other resource type.</p>
+<p>Each child of the {@code <resources>} element is converted into an application resource
+object at compile-time, which can be referenced by the value in the {@code <style>} element's
+{@code name} attribute. This example style can be referenced from an XML layout as
+{@code @style/CodeFont} (as demonstrated in the introduction above).</p>
-<p>Notice the <code>parent</code> attribute in the <code><style></code> element. This attribute lets you specify a resource from which the current style will inherit values. The style can inherit from any type of resource that contains the style(s) you want. In general, your styles should always inherit (directly or indirectly) from a standard Android style resource. This way, you only have to define the values that you want to change.</p>
+<p>The <code>parent</code> attribute in the {@code <style>} element is optional and
+specifies the resource ID of another style from which this style should inherit
+properties. You can then override the inherited style properties if you want to.</p>
-<p>Here's how you would reference the custom style from an XML layout, in this case, for an EditText element:</p>
+<p>Remember, a style that you want to use as an Activity or application theme is defined in XML
+exactly the same as a style for a View. A style such as the one defined above can be applied as a
+style for a single View or as a theme for an entire Activity or application. How to apply a style
+for a single View or as an application theme is discussed later.</p>
+
+
+<h3 id="Inheritance">Inheritance</h3>
+
+<p>The {@code parent} attribute in the {@code <style>} element lets you specify a style
+from which your style should inherit properties.
+You can use this to inherit properties from an existing style and
+then define only the properties that you want to change or add. You can
+inherit from styles that you've created yourself or from styles that are built into the
+platform. (See <a href="#PlatformStyles">Using Platform Styles and Themes</a>, below, for
+information about inheriting from styles defined by the Android platform.) For example, you can
+inherit the Android platform's default text appearance and then modify it:</p>
<pre>
-<EditText id="@+id/text1"
- style="@style/SpecialText"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:text="Hello, World!" />
+ <style name="GreenText" parent="@android:style/TextAppearance">
+ <item name="android:textColor">#00FF00</item>
+ </style>
</pre>
-<p>Now this EditText widget will be styled as defined by the XML example above.</p>
-
-
-<h2 id="themes">Themes</h2>
-
-<p>Just like styles, themes are also declared in XML <code><style></code> elements, and are referenced in the same manner.
-The difference is that you add a theme to an entire application or activity, via the <code><application></code>
-and <code><activity></code> elements in the Android Manifest —
-themes cannot be applied to individual Views.</p>
-
-<p>Here's an example declaration of a theme:</p>
+<p>If you want to inherit from styles that you've defined yourself, you <em>do not</em> have to use
+the <code>parent</code> attribute. Instead, just prefix the name of the style you want to
+inherit to the name of your new style, separated by a period. For example, to create a new style
+that inherits the <code>CodeFont</code> style defined above, but make the color red,
+you can author the new style like this:</p>
<pre>
-<?xml version="1.0" encoding="utf-8"?>
-<resources>
- <style name="CustomTheme">
- <item name="android:windowNoTitle">true</item>
- <item name="windowFrame">@drawable/screen_frame</item>
- <item name="windowBackground">@drawable/screen_background_white</item>
- <item name="panelForegroundColor">#FF000000</item>
- <item name="panelBackgroundColor">#FFFFFFFF</item>
- <item name="panelTextColor">?panelForegroundColor</item>
- <item name="panelTextSize">14</item>
- <item name="menuItemTextColor">?panelTextColor</item>
- <item name="menuItemTextSize">?panelTextSize</item>
- </style>
-</resources>
+ <style name="CodeFont.Red">
+ <item name="android:textColor">#FF0000</item>
+ </style>
</pre>
-<p>Notice the use of the at-symbol (@) and the question-mark (?) to reference resources.
-The at-symbol indicates that we're referencing a resource previously defined elsewhere (which may be from
-this project or from the Android framework).
-The question-mark indicates that we're referencing a resource value in the currently loaded theme. This
-is done by referring to a specific <code><item></code> by its <code>name</code> value. (E.g.,
-<em>panelTextColor</em> uses the same color assigned to <em>panelForegroundColor</em>, defined beforehand.)
-This technique can be used only in XML resources.
-</p>
+<p>Notice that there is no {@code parent} attribute in the {@code <style>} tag, but because
+the {@code name} attribute begins with the {@code CodeFont} style name (which
+is a style that you have created), this style inherits all style properties from that style. This
+style then overrides the {@code android:textColor} property to make the text red. You can
+reference this new style as {@code @style/CodeFont.Red}.</p>
-<h3 id="inTheManifest">Set the theme in the manifest</h3>
-<p>To set this theme for all the activities of your application, open the AndroidManifest.xml file and
+<p>You can continue inheriting like
+this as many times as you'd like, by chaining names with periods. For example, you can
+extend {@code CodeFont.Red} to be bigger, with:</p>
+<pre>
+ <style name="CodeFont.Red.Big">
+ <item name="android:textSize">30sp</item>
+ </style>
+</pre>
+<p>This inherits from both {@code CodeFont} and {@code CodeFont.Red} styles, then adds the
+{@code android:textSize} property.</p>
+
+<p class="note"><strong>Note:</strong> This technique for inheritance by chaining together
+names only works for styles defined by your own resources. You can't inherit Android built-in styles
+this way. To reference a built-in style, such as {@link android.R.style#TextAppearance}, you must
+use the {@code parent} attribute.</p>
+
+
+<h3 id="Properties">Style Properties</h3>
+
+<p>Now that you understand how a style is defined, you need to learn what kind
+of style properties—defined by the {@code <item>} element—are available.
+You're probably familiar with some already, such as {@link android.R.attr#layout_width} and
+{@link android.R.attr#textColor}. Of course, there are many more style properties you can use.</p>
+
+<p>The best place to find properties that apply to a specific {@link android.view.View} is the
+corresponding class reference, which lists all of the supported XML attributes. For example, all of the
+attributes listed in the table of
+<a href="{@docRoot}reference/android/widget/TextView.html#lattrs">TextView XML
+attributes</a> can be used in a style definition for a {@link android.widget.TextView} element (or one of
+its subclasses). One of the attributes listed in the reference is <a
+href="{@docRoot}reference/android/widget/TextView.html#attr_android:inputType">{@code
+android:inputType}</a>, so where you might normally place the <a
+href="{@docRoot}reference/android/widget/TextView.html#attr_android:inputType">{@code
+android:inputType}</a>
+attribute in an {@code <EditText>} element, like this:</p>
+<pre>
+<EditText
+ android:inputType="number"
+ ... />
+</pre>
+
+<p>You can instead create a style for the {@link android.widget.EditText} element that includes this property:</p>
+<pre>
+<style name="Numbers">
+ <item name="android:inputType">number</item>
+ ...
+</style>
+</pre>
+<p>So your XML for the layout can now implement this style:</p>
+<pre>
+<EditText
+ style="@style/Numbers"
+ ... />
+</pre>
+
+<p>This simple example may look like more work, but when you add more style properties and
+factor-in the ability to re-use the style in various places, the pay-off can be huge.</p>
+
+<p>For a reference of all available style properties, see the {@link android.R.attr}
+reference. Keep in mind that all View objects don't accept all the same style attributes, so you
+should normally refer to the specific {@link android.view.View} class for supported style
+properties. However, if you
+apply a style to a View that does not support all of the style properties, the View will
+apply only those properties that are supported and simply ignore the others.</p>
+
+<p>Some style properties, however, are not supported by any View element and can only be applied
+as a theme. These style properties apply to the entire window and not to any type of View.
+For example, style properties for a theme can hide the application title, hide the status bar,
+or change the window's background. These kind of style properties do not belong to any View object.
+To discover these theme-only style properties, look at the {@link android.R.attr} reference for
+attributes that begin with {@code window}. For instance, {@code windowNoTitle} and {@code
+windowBackground} are style properties that are effective only when the style is applied as
+a theme to an Activity or application. See the next section for information about applying a
+style as a theme.</p>
+
+<p class="note"><strong>Note:</strong> Don't forget to prefix the property names in each
+{@code <item>} element with the <code>android:</code> namespace. For example:
+{@code <item name="android:inputType">}.</p>
+
+
+
+<h2 id="ApplyingStyles">Applying Styles and Themes to the UI</h2>
+
+<p>There are two ways to set a style:</p>
+<ul>
+ <li>To an individual View, by adding the <code>style</code> attribute to a View
+ element in the XML for your layout.</li>
+ <li>Or, to an entire Activity or application, by adding the <code>android:theme</code>
+ attribute to the <code><activity></code> or <code><application></code> element
+ in the Android manifest.</li>
+</ul>
+
+<p>When you apply a style to a single {@link android.view.View} in the layout, the properties
+defined by the style are applied only to that {@link android.view.View}. If a style is applied to a
+{@link android.view.ViewGroup}, the child {@link android.view.View} elements will
+<strong>not</strong> inherit the style properties—only the element to which you directly apply
+the style will apply its properties. However, you <em>can</em> apply a style so that it
+applies to all {@link android.view.View} elements—by applying the style as a theme.</p>
+
+<p>To apply a style definition as a theme, you must apply the style to an
+{@link android.app.Activity} or application in the Android manifest. When you do so,
+every {@link android.view.View} within the Activity or
+application will apply each property that it supports. For example, if you apply the {@code
+CodeFont} style from the previous examples to an Activity, then all View elements
+that support the text style properties will apply them. Any View that does not support
+the properties will ignore them. If a View supports only some of the properties, then
+it will apply only those properties.</p>
+
+
+<h3 id="ApplyAStyle">Apply a style to a View</h3>
+
+<p>Here's how to set a style for a View in the XML layout:</p>
+
+<pre>
+<TextView
+ style="@style/CodeFont"
+ android:text="@string/hello" />
+</pre>
+
+<p>Now this TextView will be styled as defined by the style named {@code CodeFont}.
+(See the sample above, in <a href="#DefiningStyles">Defining Styles</a>.)</p>
+
+<p class="note"><strong>Note:</strong> The <code>style</code> attribute
+does <em>not</em> use the <code>android:</code> namespace prefix.</p>
+
+
+<h3 id="ApplyATheme">Apply a theme to an Activity or application</h3>
+
+<p>To set a theme for all the activities of your application, open the {@code AndroidManifest.xml} file and
edit the <code><application></code> tag to include the <code>android:theme</code> attribute with the
-theme name:</p>
+style name. For example:</p>
<pre>
<application android:theme="@style/CustomTheme">
</pre>
-<p>If you want the theme applied to just one Activity in your application, then add the theme
-attribute to the <code><activity></code> tag, instead.</p>
+<p>If you want a theme applied to just one Activity in your application, then add the
+<code>android:theme</code> attribute to the <code><activity></code> tag instead.</p>
-<p>Just as Android provides other built-in resources, there are several themes that you swap in
-without having to write one yourself. For example, you can use the Dialog theme to make your Activity
-appear like a dialog box. In the manifest, reference an Android theme like so:</p>
+<p>Just as Android provides other built-in resources, there are many pre-defined themes that you can use, to avoid
+writing them yourself. For example, you can use the {@code Dialog} theme and make your Activity
+appear like a dialog box:</p>
<pre>
<activity android:theme="@android:style/Theme.Dialog">
</pre>
-<p>If you like a theme, but want to slightly tweak it, just add the theme as the <code>parent</code> of your custom theme.
-For example, we'll modify the <code>Theme.Dialog</code> theme. To do so, create a style
-with <code>Theme.Dialog</code> as the parent:</p>
+<p>Or if you want the background to be transparent, use the Translucent theme:</p>
+
+<pre>
+<activity android:theme="@android:style/Theme.Translucent">
+</pre>
+
+<p>If you like a theme, but want to tweak it, just add the theme as the <code>parent</code>
+of your custom theme. For example, you can modify the traditional dialog theme to use your own
+background image like this:</p>
<pre>
<style name="CustomDialogTheme" parent="@android:style/Theme.Dialog">
+ <item name="android:windowBackground">@drawable/custom_dialog_background</item>
+</style>
</pre>
-<p>There it is. We've inherited the Android Dialog theme so we can adjust its styles as we like.
-So, for each item in the Dialog theme that we want to change, we re-define the value here and
-use <var>CustomDialogTheme</var> instead of <var>Theme.Dialog</var> inside the Android Manifest.</p>
-<h3 id="fromTheApp">Set the theme from the application</h3>
-<p>You can also load a theme for an Activity programmatically, if needed.
-To do so, use the {@link android.app.Activity#setTheme(int) setTheme()}
-method. Note that, when doing so, you must be sure to set the theme <em>before</em>
+<p>Now use {@code CustomDialogTheme} instead of {@code Theme.Dialog} inside the Android
+Manifest:</p>
+
+<pre>
+<activity android:theme="@style/CustomDialogTheme">
+</pre>
+
+
+<!-- This currently has some bugs
+
+<h3 id="setThemeFromTheApp">Set the theme from the application</h3>
+
+<p>We recommend that you set your themes in you Android manifest, as described above, because it's simple and
+keeps your program code focused on application functionality, rather than style. But if it's necessary
+for you to change your theme programatically (perhaps based on a user preference), you can.</p>
+
+<p>To set the theme in your program code, use the {@link android.content.ContextWrapper#setTheme(int)}
+method and pass it the theme resource ID. Note that, when doing so, you must be sure to set the theme <em>before</em>
instantiating any Views in the context, for example, before calling
<code>setContentView(View)</code> or <code>inflate(int, ViewGroup)</code>. This ensures that
the system applies the same theme for all of your UI screens. Here's an example:</p>
@@ -164,12 +349,40 @@
you want to apply a theme to your main screen, doing so in XML
is a better approach. </p>
-<p>For detailed information about custom styles and themes and referencing them from your application, see
+-->
+
+
+
+<h2 id="PlatformStyles">Using Platform Styles and Themes</h2>
+
+<p>The Android platform provides a large collection of styles and themes that you can
+use in your applications. You can find a reference of all available styles in the
+{@link android.R.style} class. To use the styles listed here, replace all underscores in
+the style name with a period. For example, you can apply the
+{@link android.R.style#Theme_NoTitleBar} theme with
+{@code "@android:style/Theme.NoTitleBar"}.</p>
+
+<p>The {@link android.R.style} reference, however, is not well documented and does not
+thoroughly describe the styles, so viewing the actual source code for these styles and
+themes will give you a better understanding of what style properties each one provides.
+For a better reference to the Android styles and themes, see the following source code:</p>
+<ul>
+ <li><a href="http://android.git.kernel.org/?p=platform/frameworks/base.git;a=blob;f=core/res/res/values/styles.xml;h=d7b654e49809cb97a35682754b1394af5c8bc88b;hb=HEAD">Android Styles (styles.xml)</a></li>
+ <li><a href="http://android.git.kernel.org/?p=platform/frameworks/base.git;a=blob;f=core/res/res/values/themes.xml;h=6b3d7407d1c895a3c297e60d5beac98e2d34c271;hb=HEAD">Android Themes (themes.xml)</a></li>
+</ul>
+
+<p>These files will help you learn through example. For instance, in the Android themes source code,
+you'll find a declaration for <code><style name="Theme.Dialog"></code>. In this definition,
+you'll see all of the properties that are used to style dialogs that are used by the Android
+framework.</p>
+
+<p>For more information about the syntax used to create styles in XML, see
<a href="{@docRoot}guide/topics/resources/available-resources.html#stylesandthemes">Available Resource Types:
Style and Themes</a>.</p>
-<p>For information about default themes and styles available, see {@link android.R.style}.</p>
-
+<p>For a reference of available style attributes that you can use to define a style or theme
+(e.g., "windowBackground" or "textAppearance"), see {@link android.R.attr} or the respective
+View class for which you are creating a style.</p>
diff --git a/include/media/stagefright/AudioPlayer.h b/include/media/stagefright/AudioPlayer.h
index 26fcc95..843e051 100644
--- a/include/media/stagefright/AudioPlayer.h
+++ b/include/media/stagefright/AudioPlayer.h
@@ -41,9 +41,6 @@
// Caller retains ownership of "source".
void setSource(const sp<MediaSource> &source);
- void setListenerCallback(
- void (*notify)(void *cookie, int what), void *cookie);
-
// Return time in us.
virtual int64_t getRealTimeUs();
@@ -63,6 +60,9 @@
status_t seekTo(int64_t time_us);
+ bool isSeeking();
+ bool reachedEOS();
+
private:
sp<MediaSource> mSource;
AudioTrack *mAudioTrack;
@@ -80,13 +80,11 @@
int64_t mPositionTimeRealUs;
bool mSeeking;
+ bool mReachedEOS;
int64_t mSeekTimeUs;
bool mStarted;
- void (*mListenerCallback)(void *cookie, int what);
- void *mListenerCookie;
-
sp<MediaPlayerBase::AudioSink> mAudioSink;
static void AudioCallback(int event, void *user, void *info);
diff --git a/media/java/android/media/ExifInterface.java b/media/java/android/media/ExifInterface.java
index e9d3372..4292754 100644
--- a/media/java/android/media/ExifInterface.java
+++ b/media/java/android/media/ExifInterface.java
@@ -28,22 +28,37 @@
* This is a class for reading and writing Exif tags in a JPEG file.
*/
public class ExifInterface {
-
// The Exif tag names
+ /** Type is int. */
public static final String TAG_ORIENTATION = "Orientation";
+ /** Type is String. */
public static final String TAG_DATETIME = "DateTime";
+ /** Type is String. */
public static final String TAG_MAKE = "Make";
+ /** Type is String. */
public static final String TAG_MODEL = "Model";
+ /** Type is int. */
public static final String TAG_FLASH = "Flash";
+ /** Type is int. */
public static final String TAG_IMAGE_WIDTH = "ImageWidth";
+ /** Type is int. */
public static final String TAG_IMAGE_LENGTH = "ImageLength";
+ /** String. Format is "num1/denom1,num2/denom2,num3/denom3". */
public static final String TAG_GPS_LATITUDE = "GPSLatitude";
+ /** String. Format is "num1/denom1,num2/denom2,num3/denom3". */
public static final String TAG_GPS_LONGITUDE = "GPSLongitude";
+ /** Type is String. */
public static final String TAG_GPS_LATITUDE_REF = "GPSLatitudeRef";
+ /** Type is String. */
public static final String TAG_GPS_LONGITUDE_REF = "GPSLongitudeRef";
+ /** Type is String. */
public static final String TAG_GPS_TIMESTAMP = "GPSTimeStamp";
+ /** Type is String. */
public static final String TAG_GPS_DATESTAMP = "GPSDateStamp";
+ /** Type is int. */
public static final String TAG_WHITE_BALANCE = "WhiteBalance";
+ /** Type is rational. */
+ public static final String TAG_FOCAL_LENGTH = "FocalLength";
// Constants used for the Orientation Exif tag.
public static final int ORIENTATION_UNDEFINED = 0;
@@ -114,6 +129,29 @@
}
/**
+ * Returns the double value of the specified rational tag. If there is no
+ * such tag in the JPEG file or the value cannot be parsed as double, return
+ * <var>defaultValue</var>.
+ *
+ * @param tag the name of the tag.
+ * @param defaultValue the value to return if the tag is not available.
+ */
+ public double getAttributeDouble(String tag, double defaultValue) {
+ String value = mAttributes.get(tag);
+ if (value == null) return defaultValue;
+ try {
+ int index = value.indexOf("/");
+ if (index == -1) return defaultValue;
+ double denom = Double.parseDouble(value.substring(index + 1));
+ if (denom == 0) return defaultValue;
+ double num = Double.parseDouble(value.substring(0, index));
+ return num / denom;
+ } catch (NumberFormatException ex) {
+ return defaultValue;
+ }
+ }
+
+ /**
* Set the value of the specified tag.
*
* @param tag the name of the tag.
diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk
index dbb52c6..b25ce67 100644
--- a/media/libstagefright/Android.mk
+++ b/media/libstagefright/Android.mk
@@ -60,8 +60,6 @@
libsonivox \
libvorbisidec
-ifeq ($(BUILD_WITH_FULL_STAGEFRIGHT),true)
-
LOCAL_STATIC_LIBRARIES := \
libstagefright_aacdec \
libstagefright_amrnbdec \
@@ -69,12 +67,18 @@
libstagefright_amrwbdec \
libstagefright_avcdec \
libstagefright_m4vh263dec \
- libstagefright_mp3dec \
- libstagefright_id3
+ libstagefright_mp3dec
LOCAL_SHARED_LIBRARIES += \
libstagefright_amrnb_common \
- libstagefright_avc_common \
+ libstagefright_avc_common
+
+ifeq ($(BUILD_WITH_FULL_STAGEFRIGHT),true)
+
+LOCAL_STATIC_LIBRARIES += \
+ libstagefright_id3
+
+LOCAL_SHARED_LIBRARIES += \
libstagefright_color_conversion
endif
diff --git a/media/libstagefright/AudioPlayer.cpp b/media/libstagefright/AudioPlayer.cpp
index 01578c1..e7351dc 100644
--- a/media/libstagefright/AudioPlayer.cpp
+++ b/media/libstagefright/AudioPlayer.cpp
@@ -37,6 +37,7 @@
mPositionTimeMediaUs(-1),
mPositionTimeRealUs(-1),
mSeeking(false),
+ mReachedEOS(false),
mStarted(false),
mAudioSink(audioSink) {
}
@@ -47,12 +48,6 @@
}
}
-void AudioPlayer::setListenerCallback(
- void (*notify)(void *cookie, int what), void *cookie) {
- mListenerCallback = notify;
- mListenerCookie = cookie;
-}
-
void AudioPlayer::setSource(const sp<MediaSource> &source) {
CHECK_EQ(mSource, NULL);
mSource = source;
@@ -172,6 +167,7 @@
mPositionTimeMediaUs = -1;
mPositionTimeRealUs = -1;
mSeeking = false;
+ mReachedEOS = false;
mStarted = false;
}
@@ -180,6 +176,16 @@
static_cast<AudioPlayer *>(user)->AudioCallback(event, info);
}
+bool AudioPlayer::isSeeking() {
+ Mutex::Autolock autoLock(mLock);
+ return mSeeking;
+}
+
+bool AudioPlayer::reachedEOS() {
+ Mutex::Autolock autoLock(mLock);
+ return mReachedEOS;
+}
+
// static
void AudioPlayer::AudioSinkCallback(
MediaPlayerBase::AudioSink *audioSink,
@@ -203,6 +209,11 @@
LOGV("AudioCallback");
}
+ if (mReachedEOS) {
+ memset(data, 0, size);
+ return;
+ }
+
size_t size_done = 0;
size_t size_remaining = size;
while (size_remaining > 0) {
@@ -227,30 +238,21 @@
CHECK((err == OK && mInputBuffer != NULL)
|| (err != OK && mInputBuffer == NULL));
- if (mSeeking) {
- mSeeking = false;
+ Mutex::Autolock autoLock(mLock);
- if (mListenerCallback) {
- (*mListenerCallback)(mListenerCookie, SEEK_COMPLETE);
- }
- }
+ mSeeking = false;
if (err != OK) {
- if (mListenerCallback) {
- (*mListenerCallback)(mListenerCookie, REACHED_EOS);
- }
-
+ mReachedEOS = true;
memset((char *)data + size_done, 0, size_remaining);
break;
}
- Mutex::Autolock autoLock(mLock);
CHECK(mInputBuffer->meta_data()->findInt64(
kKeyTime, &mPositionTimeMediaUs));
mPositionTimeRealUs =
- -mLatencyUs
- + ((mNumFramesPlayed + size_done / mFrameSize) * 1000000)
+ ((mNumFramesPlayed + size_done / mFrameSize) * 1000000)
/ mSampleRate;
LOGV("buffer->size() = %d, "
@@ -302,7 +304,12 @@
return 0;
}
- return mPositionTimeMediaUs + (getRealTimeUsLocked() - mPositionTimeRealUs);
+ int64_t realTimeOffset = getRealTimeUsLocked() - mPositionTimeRealUs;
+ if (realTimeOffset < 0) {
+ realTimeOffset = 0;
+ }
+
+ return mPositionTimeMediaUs + realTimeOffset;
}
bool AudioPlayer::getMediaTimeMapping(
@@ -319,6 +326,7 @@
Mutex::Autolock autoLock(mLock);
mSeeking = true;
+ mReachedEOS = false;
mSeekTimeUs = time_us;
return OK;
diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp
index 4e7738e..2b403f8 100644
--- a/media/libstagefright/AwesomePlayer.cpp
+++ b/media/libstagefright/AwesomePlayer.cpp
@@ -121,6 +121,8 @@
mStreamDoneEventPending = false;
mBufferingEvent = new AwesomeEvent(this, 2);
mBufferingEventPending = false;
+ mCheckAudioStatusEvent = new AwesomeEvent(this, 3);
+ mAudioStatusEventPending = false;
mQueue.start();
@@ -140,6 +142,8 @@
mVideoEventPending = false;
mQueue.cancelEvent(mStreamDoneEvent->eventID());
mStreamDoneEventPending = false;
+ mQueue.cancelEvent(mCheckAudioStatusEvent->eventID());
+ mAudioStatusEventPending = false;
if (!keepBufferingGoing) {
mQueue.cancelEvent(mBufferingEvent->eventID());
@@ -283,29 +287,6 @@
mPrefetcher.clear();
}
-// static
-void AwesomePlayer::AudioNotify(void *_me, int what) {
- AwesomePlayer *me = (AwesomePlayer *)_me;
-
- Mutex::Autolock autoLock(me->mLock);
-
- switch (what) {
- case AudioPlayer::REACHED_EOS:
- me->postStreamDoneEvent_l();
- break;
-
- case AudioPlayer::SEEK_COMPLETE:
- {
- me->notifyListener_l(MEDIA_SEEK_COMPLETE);
- break;
- }
-
- default:
- CHECK(!"should not be here.");
- break;
- }
-}
-
void AwesomePlayer::notifyListener_l(int msg, int ext1) {
if (mListener != NULL) {
sp<MediaPlayerBase> listener = mListener.promote();
@@ -323,7 +304,7 @@
if (mDurationUs >= 0) {
int64_t cachedDurationUs = mPrefetcher->getCachedDurationUs();
int64_t positionUs = 0;
- if (mVideoRenderer != NULL) {
+ if (mVideoSource != NULL) {
positionUs = mVideoTimeUs;
} else if (mAudioPlayer != NULL) {
positionUs = mAudioPlayer->getMediaTimeUs();
@@ -347,7 +328,7 @@
if (mFlags & LOOPING) {
seekTo_l(0);
- if (mVideoRenderer != NULL) {
+ if (mVideoSource != NULL) {
postVideoEvent_l();
}
} else {
@@ -373,10 +354,6 @@
if (mAudioPlayer == NULL) {
if (mAudioSink != NULL) {
mAudioPlayer = new AudioPlayer(mAudioSink);
-
- mAudioPlayer->setListenerCallback(
- &AwesomePlayer::AudioNotify, this);
-
mAudioPlayer->setSource(mAudioSource);
status_t err = mAudioPlayer->start();
@@ -393,10 +370,15 @@
mTimeSource = mAudioPlayer;
deferredAudioSeek = true;
+
+ mWatchForAudioSeekComplete = false;
+ mWatchForAudioEOS = true;
}
} else {
mAudioPlayer->resume();
}
+
+ postCheckAudioStatusEvent_l();
}
if (mTimeSource == NULL && mAudioPlayer == NULL) {
@@ -404,14 +386,8 @@
}
if (mVideoSource != NULL) {
- if (mVideoRenderer == NULL) {
- initRenderer_l();
- }
-
- if (mVideoRenderer != NULL) {
- // Kick off video playback
- postVideoEvent_l();
- }
+ // Kick off video playback
+ postVideoEvent_l();
}
if (deferredAudioSeek) {
@@ -532,7 +508,7 @@
status_t AwesomePlayer::getPosition(int64_t *positionUs) {
Mutex::Autolock autoLock(mLock);
- if (mVideoRenderer != NULL) {
+ if (mVideoSource != NULL) {
*positionUs = mVideoTimeUs;
} else if (mAudioPlayer != NULL) {
*positionUs = mAudioPlayer->getMediaTimeUs();
@@ -558,9 +534,11 @@
}
void AwesomePlayer::seekAudioIfNecessary_l() {
- if (mSeeking && mVideoRenderer == NULL && mAudioPlayer != NULL) {
+ if (mSeeking && mVideoSource == NULL && mAudioPlayer != NULL) {
mAudioPlayer->seekTo(mSeekTimeUs);
+ mWatchForAudioSeekComplete = true;
+ mWatchForAudioEOS = true;
mSeeking = false;
}
}
@@ -652,6 +630,9 @@
} else if (code == 2) {
onBufferingUpdate();
return;
+ } else if (code == 3) {
+ onCheckAudioStatus();
+ return;
}
Mutex::Autolock autoLock(mLock);
@@ -687,7 +668,9 @@
if (err == INFO_FORMAT_CHANGED) {
LOGV("VideoSource signalled format change.");
- initRenderer_l();
+ if (mVideoRenderer != NULL) {
+ initRenderer_l();
+ }
continue;
}
@@ -718,6 +701,8 @@
LOGV("seeking audio to %lld us (%.2f secs).", timeUs, timeUs / 1E6);
mAudioPlayer->seekTo(timeUs);
+ mWatchForAudioSeekComplete = true;
+ mWatchForAudioEOS = true;
} else {
// If we're playing video only, report seek complete now,
// otherwise audio player will notify us later.
@@ -762,7 +747,13 @@
return;
}
- mVideoRenderer->render(mVideoBuffer);
+ if (mVideoRenderer == NULL) {
+ initRenderer_l();
+ }
+
+ if (mVideoRenderer != NULL) {
+ mVideoRenderer->render(mVideoBuffer);
+ }
if (mLastVideoBuffer) {
mLastVideoBuffer->release();
@@ -803,5 +794,30 @@
mQueue.postEventWithDelay(mBufferingEvent, 1000000ll);
}
+void AwesomePlayer::postCheckAudioStatusEvent_l() {
+ if (mAudioStatusEventPending) {
+ return;
+ }
+ mAudioStatusEventPending = true;
+ mQueue.postEventWithDelay(mCheckAudioStatusEvent, 100000ll);
+}
+
+void AwesomePlayer::onCheckAudioStatus() {
+ Mutex::Autolock autoLock(mLock);
+ mAudioStatusEventPending = false;
+
+ if (mWatchForAudioSeekComplete && !mAudioPlayer->isSeeking()) {
+ mWatchForAudioSeekComplete = false;
+ notifyListener_l(MEDIA_SEEK_COMPLETE);
+ }
+
+ if (mWatchForAudioEOS && mAudioPlayer->reachedEOS()) {
+ mWatchForAudioEOS = false;
+ postStreamDoneEvent_l();
+ }
+
+ postCheckAudioStatusEvent_l();
+}
+
} // namespace android
diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp
index 90bbdfe..0355a82 100644
--- a/media/libstagefright/OMXCodec.cpp
+++ b/media/libstagefright/OMXCodec.cpp
@@ -18,7 +18,6 @@
#define LOG_TAG "OMXCodec"
#include <utils/Log.h>
-#if BUILD_WITH_FULL_STAGEFRIGHT
#include "include/AACDecoder.h"
#include "include/AMRNBDecoder.h"
#include "include/AMRNBEncoder.h"
@@ -26,7 +25,6 @@
#include "include/AVCDecoder.h"
#include "include/M4vH263Decoder.h"
#include "include/MP3Decoder.h"
-#endif
#include "include/ESDS.h"
@@ -56,9 +54,6 @@
const char *codec;
};
-#if BUILD_WITH_FULL_STAGEFRIGHT
-#define OPTIONAL(x,y) { x, y },
-
#define FACTORY_CREATE(name) \
static sp<MediaSource> Make##name(const sp<MediaSource> &source) { \
return new name(source); \
@@ -103,42 +98,30 @@
#undef FACTORY_REF
#undef FACTORY_CREATE
-#else
-#define OPTIONAL(x,y)
-#endif
-
static const CodecInfo kDecoderInfo[] = {
{ MEDIA_MIMETYPE_IMAGE_JPEG, "OMX.TI.JPEG.decode" },
{ MEDIA_MIMETYPE_AUDIO_MPEG, "OMX.TI.MP3.decode" },
- OPTIONAL(MEDIA_MIMETYPE_AUDIO_MPEG, "MP3Decoder")
- { MEDIA_MIMETYPE_AUDIO_MPEG, "OMX.PV.mp3dec" },
+ { MEDIA_MIMETYPE_AUDIO_MPEG, "MP3Decoder" },
{ MEDIA_MIMETYPE_AUDIO_AMR_NB, "OMX.TI.AMR.decode" },
- OPTIONAL(MEDIA_MIMETYPE_AUDIO_AMR_NB, "AMRNBDecoder")
- { MEDIA_MIMETYPE_AUDIO_AMR_NB, "OMX.PV.amrdec" },
+ { MEDIA_MIMETYPE_AUDIO_AMR_NB, "AMRNBDecoder" },
{ MEDIA_MIMETYPE_AUDIO_AMR_WB, "OMX.TI.WBAMR.decode" },
- OPTIONAL(MEDIA_MIMETYPE_AUDIO_AMR_WB, "AMRWBDecoder")
- { MEDIA_MIMETYPE_AUDIO_AMR_WB, "OMX.PV.amrdec" },
+ { MEDIA_MIMETYPE_AUDIO_AMR_WB, "AMRWBDecoder" },
{ MEDIA_MIMETYPE_AUDIO_AAC, "OMX.TI.AAC.decode" },
- OPTIONAL(MEDIA_MIMETYPE_AUDIO_AAC, "AACDecoder")
- { MEDIA_MIMETYPE_AUDIO_AAC, "OMX.PV.aacdec" },
+ { MEDIA_MIMETYPE_AUDIO_AAC, "AACDecoder" },
{ MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.qcom.video.decoder.mpeg4" },
{ MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.TI.Video.Decoder" },
- OPTIONAL(MEDIA_MIMETYPE_VIDEO_MPEG4, "M4vH263Decoder")
- { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.PV.mpeg4dec" },
+ { MEDIA_MIMETYPE_VIDEO_MPEG4, "M4vH263Decoder" },
{ MEDIA_MIMETYPE_VIDEO_H263, "OMX.qcom.video.decoder.h263" },
{ MEDIA_MIMETYPE_VIDEO_H263, "OMX.TI.Video.Decoder" },
- OPTIONAL(MEDIA_MIMETYPE_VIDEO_H263, "M4vH263Decoder")
- { MEDIA_MIMETYPE_VIDEO_H263, "OMX.PV.h263dec" },
+ { MEDIA_MIMETYPE_VIDEO_H263, "M4vH263Decoder" },
{ MEDIA_MIMETYPE_VIDEO_AVC, "OMX.qcom.video.decoder.avc" },
{ MEDIA_MIMETYPE_VIDEO_AVC, "OMX.TI.Video.Decoder" },
- OPTIONAL(MEDIA_MIMETYPE_VIDEO_AVC, "AVCDecoder")
- { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.PV.avcdec" },
+ { MEDIA_MIMETYPE_VIDEO_AVC, "AVCDecoder" },
};
static const CodecInfo kEncoderInfo[] = {
{ MEDIA_MIMETYPE_AUDIO_AMR_NB, "OMX.TI.AMR.encode" },
- OPTIONAL(MEDIA_MIMETYPE_AUDIO_AMR_NB, "AMRNBEncoder")
- { MEDIA_MIMETYPE_AUDIO_AMR_NB, "OMX.PV.amrencnb" },
+ { MEDIA_MIMETYPE_AUDIO_AMR_NB, "AMRNBEncoder" },
{ MEDIA_MIMETYPE_AUDIO_AMR_WB, "OMX.TI.WBAMR.encode" },
{ MEDIA_MIMETYPE_AUDIO_AAC, "OMX.TI.AAC.encode" },
{ MEDIA_MIMETYPE_AUDIO_AAC, "OMX.PV.aacenc" },
diff --git a/media/libstagefright/codecs/Android.mk b/media/libstagefright/codecs/Android.mk
index 1fa7e93..2e431205 100644
--- a/media/libstagefright/codecs/Android.mk
+++ b/media/libstagefright/codecs/Android.mk
@@ -1,8 +1,4 @@
-ifeq ($(BUILD_WITH_FULL_STAGEFRIGHT),true)
-
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
include $(call all-makefiles-under,$(LOCAL_PATH))
-
-endif
diff --git a/media/libstagefright/id3/Android.mk b/media/libstagefright/id3/Android.mk
index 3c47e2e..add8f3c 100644
--- a/media/libstagefright/id3/Android.mk
+++ b/media/libstagefright/id3/Android.mk
@@ -1,3 +1,5 @@
+ifeq ($(BUILD_WITH_FULL_STAGEFRIGHT),true)
+
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
@@ -25,3 +27,4 @@
include $(BUILD_EXECUTABLE)
+endif
diff --git a/media/libstagefright/include/AwesomePlayer.h b/media/libstagefright/include/AwesomePlayer.h
index 8bd6594..75e71e6 100644
--- a/media/libstagefright/include/AwesomePlayer.h
+++ b/media/libstagefright/include/AwesomePlayer.h
@@ -109,16 +109,22 @@
bool mSeeking;
int64_t mSeekTimeUs;
+ bool mWatchForAudioSeekComplete;
+ bool mWatchForAudioEOS;
+
sp<TimedEventQueue::Event> mVideoEvent;
bool mVideoEventPending;
sp<TimedEventQueue::Event> mStreamDoneEvent;
bool mStreamDoneEventPending;
sp<TimedEventQueue::Event> mBufferingEvent;
bool mBufferingEventPending;
+ sp<TimedEventQueue::Event> mCheckAudioStatusEvent;
+ bool mAudioStatusEventPending;
void postVideoEvent_l(int64_t delayUs = -1);
void postBufferingEvent_l();
void postStreamDoneEvent_l();
+ void postCheckAudioStatusEvent_l();
MediaBuffer *mLastVideoBuffer;
MediaBuffer *mVideoBuffer;
@@ -138,13 +144,12 @@
status_t setVideoSource(sp<MediaSource> source);
void onEvent(int32_t code);
-
- static void AudioNotify(void *me, int what);
void onStreamDone();
void notifyListener_l(int msg, int ext1 = 0);
void onBufferingUpdate();
+ void onCheckAudioStatus();
AwesomePlayer(const AwesomePlayer &);
AwesomePlayer &operator=(const AwesomePlayer &);
diff --git a/native/graphics/jni/Android.mk b/native/graphics/jni/Android.mk
new file mode 100644
index 0000000..b7ae4a9
--- /dev/null
+++ b/native/graphics/jni/Android.mk
@@ -0,0 +1,36 @@
+BASE_PATH := $(call my-dir)
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_PRELINK_MODULE := false
+
+# setup for skia optimizations
+#
+ifneq ($(ARCH_ARM_HAVE_VFP),true)
+ LOCAL_CFLAGS += -DSK_SOFTWARE_FLOAT
+endif
+
+ifeq ($(ARCH_ARM_HAVE_NEON),true)
+ LOCAL_CFLAGS += -D__ARM_HAVE_NEON
+endif
+
+# our source files
+#
+LOCAL_SRC_FILES:= \
+ bitmap.cpp
+
+LOCAL_SHARED_LIBRARIES := \
+ libandroid_runtime \
+ libskia
+
+LOCAL_C_INCLUDES += \
+ external/skia/include/core \
+ frameworks/base/native/include \
+ frameworks/base/core/jni/android/graphics \
+ dalvik/libnativehelper/include/nativehelper
+
+LOCAL_MODULE:= libjnigraphics
+
+include $(BUILD_SHARED_LIBRARY)
+
diff --git a/native/graphics/jni/bitmap.cpp b/native/graphics/jni/bitmap.cpp
new file mode 100644
index 0000000..fd73430
--- /dev/null
+++ b/native/graphics/jni/bitmap.cpp
@@ -0,0 +1,94 @@
+/*
+ * 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.
+ */
+
+#include <android/bitmap.h>
+#include <GraphicsJNI.h>
+
+int AndroidBitmap_getInfo(JNIEnv* env, jobject jbitmap,
+ AndroidBitmapInfo* info) {
+ if (NULL == env || NULL == jbitmap) {
+ return ANDROID_BITMAP_RESULT_BAD_PARAMETER;
+ }
+
+ SkBitmap* bm = GraphicsJNI::getNativeBitmap(env, jbitmap);
+ if (NULL == bm) {
+ return ANDROID_BITMAP_RESULT_JNI_EXCEPTION;
+ }
+
+ if (info) {
+ info->width = bm->width();
+ info->height = bm->height();
+ info->stride = bm->rowBytes();
+ info->flags = 0;
+
+ switch (bm->config()) {
+ case SkBitmap::kARGB_8888_Config:
+ info->format = ANDROID_BITMAP_FORMAT_RGBA_8888;
+ break;
+ case SkBitmap::kRGB_565_Config:
+ info->format = ANDROID_BITMAP_FORMAT_RGB_565;
+ break;
+ case SkBitmap::kARGB_4444_Config:
+ info->format = ANDROID_BITMAP_FORMAT_RGBA_4444;
+ break;
+ case SkBitmap::kA8_Config:
+ info->format = ANDROID_BITMAP_FORMAT_A_8;
+ break;
+ default:
+ info->format = ANDROID_BITMAP_FORMAT_NONE;
+ break;
+ }
+ }
+ return ANDROID_BITMAP_RESUT_SUCCESS;
+}
+
+int AndroidBitmap_lockPixels(JNIEnv* env, jobject jbitmap, void** addrPtr) {
+ if (NULL == env || NULL == jbitmap) {
+ return ANDROID_BITMAP_RESULT_BAD_PARAMETER;
+ }
+
+ SkBitmap* bm = GraphicsJNI::getNativeBitmap(env, jbitmap);
+ if (NULL == bm) {
+ return ANDROID_BITMAP_RESULT_JNI_EXCEPTION;
+ }
+
+ bm->lockPixels();
+ void* addr = bm->getPixels();
+ if (NULL == addr) {
+ bm->unlockPixels();
+ return ANDROID_BITMAP_RESULT_ALLOCATION_FAILED;
+ }
+
+ if (addrPtr) {
+ *addrPtr = addr;
+ }
+ return ANDROID_BITMAP_RESUT_SUCCESS;
+}
+
+int AndroidBitmap_unlockPixels(JNIEnv* env, jobject jbitmap) {
+ if (NULL == env || NULL == jbitmap) {
+ return ANDROID_BITMAP_RESULT_BAD_PARAMETER;
+ }
+
+ SkBitmap* bm = GraphicsJNI::getNativeBitmap(env, jbitmap);
+ if (NULL == bm) {
+ return ANDROID_BITMAP_RESULT_JNI_EXCEPTION;
+ }
+
+ bm->unlockPixels();
+ return ANDROID_BITMAP_RESUT_SUCCESS;
+}
+
diff --git a/native/include/android/bitmap.h b/native/include/android/bitmap.h
new file mode 100644
index 0000000..5078277
--- /dev/null
+++ b/native/include/android/bitmap.h
@@ -0,0 +1,79 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_BITMAP_H
+#define ANDROID_BITMAP_H
+
+#include <stdint.h>
+#include <jni.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define ANDROID_BITMAP_RESUT_SUCCESS 0
+#define ANDROID_BITMAP_RESULT_BAD_PARAMETER -1
+#define ANDROID_BITMAP_RESULT_JNI_EXCEPTION -2
+#define ANDROID_BITMAP_RESULT_ALLOCATION_FAILED -3
+
+enum AndroidBitmapFormat {
+ ANDROID_BITMAP_FORMAT_NONE = 0,
+ ANDROID_BITMAP_FORMAT_RGBA_8888 = 1,
+ ANDROID_BITMAP_FORMAT_RGB_565 = 4,
+ ANDROID_BITMAP_FORMAT_RGBA_4444 = 7,
+ ANDROID_BITMAP_FORMAT_A_8 = 8,
+};
+
+typedef struct {
+ uint32_t width;
+ uint32_t height;
+ uint32_t stride;
+ int32_t format;
+ uint32_t flags; // 0 for now
+} AndroidBitmapInfo;
+
+/**
+ * Given a java bitmap object, fill out the AndroidBitmap struct for it.
+ * If the call fails, the info parameter will be ignored
+ */
+int AndroidBitmap_getInfo(JNIEnv* env, jobject jbitmap,
+ AndroidBitmapInfo* info);
+
+/**
+ * Given a java bitmap object, attempt to lock the pixel address.
+ * Locking will ensure that the memory for the pixels will not move
+ * until the unlockPixels call, and ensure that, if the pixels had been
+ * previously purged, they will have been restored.
+ *
+ * If this call succeeds, it must be balanced by a call to
+ * AndroidBitmap_unlockPixels, after which time the address of the pixels should
+ * no longer be used.
+ *
+ * If this succeeds, *addrPtr will be set to the pixel address. If the call
+ * fails, addrPtr will be ignored.
+ */
+int AndroidBitmap_lockPixels(JNIEnv* env, jobject jbitmap, void** addrPtr);
+
+/**
+ * Call this to balanace a successful call to AndroidBitmap_lockPixels
+ */
+int AndroidBitmap_unlockPixels(JNIEnv* env, jobject jbitmap);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/preloaded-classes b/preloaded-classes
index 0c84904..3e2eb13 100644
--- a/preloaded-classes
+++ b/preloaded-classes
@@ -464,7 +464,6 @@
android.webkit.CallbackProxy
android.webkit.CookieManager
android.webkit.CookieSyncManager
-android.webkit.HttpDateTime
android.webkit.JWebCoreJavaBridge
android.webkit.LoadListener
android.webkit.MimeTypeMap
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java
index 0562c55..6795bdd 100644
--- a/services/java/com/android/server/BackupManagerService.java
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -118,6 +118,7 @@
boolean mEnabled; // access to this is synchronized on 'this'
boolean mProvisioned;
+ boolean mAutoRestore;
PowerManager.WakeLock mWakelock;
HandlerThread mHandlerThread = new HandlerThread("backup", Process.THREAD_PRIORITY_BACKGROUND);
BackupHandler mBackupHandler;
@@ -175,11 +176,21 @@
public IBackupTransport transport;
public IRestoreObserver observer;
public long token;
+ public PackageInfo pkgInfo;
+
+ RestoreParams(IBackupTransport _transport, IRestoreObserver _obs,
+ long _token, PackageInfo _pkg) {
+ transport = _transport;
+ observer = _obs;
+ token = _token;
+ pkgInfo = _pkg;
+ }
RestoreParams(IBackupTransport _transport, IRestoreObserver _obs, long _token) {
transport = _transport;
observer = _obs;
token = _token;
+ pkgInfo = null;
}
}
@@ -209,10 +220,16 @@
File mJournalDir;
File mJournal;
- // Keep a log of all the apps we've ever backed up
+ // Keep a log of all the apps we've ever backed up, and what the
+ // dataset tokens are for both the current backup dataset and
+ // the ancestral dataset.
private File mEverStored;
HashSet<String> mEverStoredApps = new HashSet<String>();
+ File mTokenFile;
+ long mAncestralToken = 0;
+ long mCurrentToken = 0;
+
// Persistently track the need to do a full init
static final String INIT_SENTINEL_FILE_NAME = "_need_init_";
HashSet<String> mPendingInits = new HashSet<String>(); // transport names
@@ -276,7 +293,7 @@
RestoreParams params = (RestoreParams)msg.obj;
Log.d(TAG, "MSG_RUN_RESTORE observer=" + params.observer);
(new PerformRestoreTask(params.transport, params.observer,
- params.token)).run();
+ params.token, params.pkgInfo)).run();
break;
}
@@ -340,6 +357,8 @@
Settings.Secure.BACKUP_ENABLED, 0) != 0;
mProvisioned = Settings.Secure.getInt(context.getContentResolver(),
Settings.Secure.BACKUP_PROVISIONED, 0) != 0;
+ mAutoRestore = Settings.Secure.getInt(context.getContentResolver(),
+ Settings.Secure.BACKUP_AUTO_RESTORE, 0) != 0;
// If Encrypted file systems is enabled or disabled, this call will return the
// correct directory.
mBaseStateDir = new File(Environment.getSecureDataDirectory(), "backup");
@@ -472,6 +491,16 @@
private void initPackageTracking() {
if (DEBUG) Log.v(TAG, "Initializing package tracking");
+ // Remember our ancestral dataset
+ mTokenFile = new File(mBaseStateDir, "ancestral");
+ try {
+ RandomAccessFile tf = new RandomAccessFile(mTokenFile, "r");
+ mAncestralToken = tf.readLong();
+ mCurrentToken = tf.readLong();
+ } catch (IOException e) {
+ Log.w(TAG, "Unable to read token file", e);
+ }
+
// Keep a log of what apps we've ever backed up. Because we might have
// rebooted in the middle of an operation that was removing something from
// this log, we sanity-check its contents here and reconstruct it.
@@ -604,6 +633,9 @@
mEverStoredApps.clear();
mEverStored.delete();
+ mCurrentToken = 0;
+ writeRestoreTokens();
+
// Remove all the state files
for (File sf : stateFileDir.listFiles()) {
// ... but don't touch the needs-init sentinel
@@ -877,7 +909,7 @@
addPackageParticipantsLockedInner(packageName, allApps);
}
- // Called from the backup thread: record that the given app has been successfully
+ // Called from the backup task: record that the given app has been successfully
// backed up at least once
void logBackupComplete(String packageName) {
if (packageName.equals(PACKAGE_MANAGER_SENTINEL)) return;
@@ -935,6 +967,18 @@
}
}
+ // Record the current and ancestral backup tokens persistently
+ void writeRestoreTokens() {
+ try {
+ RandomAccessFile af = new RandomAccessFile(mTokenFile, "rwd");
+ af.writeLong(mAncestralToken);
+ af.writeLong(mCurrentToken);
+ af.close();
+ } catch (IOException e) {
+ Log.w(TAG, "Unable to write token file:", e);
+ }
+ }
+
// Return the given transport
private IBackupTransport getTransport(String transportName) {
synchronized (mTransports) {
@@ -1151,6 +1195,16 @@
Log.e(TAG, "Error in backup thread", e);
status = BackupConstants.TRANSPORT_ERROR;
} finally {
+ // If everything actually went through and this is the first time we've
+ // done a backup, we can now record what the current backup dataset token
+ // is.
+ if ((mCurrentToken == 0) && (status != BackupConstants.TRANSPORT_OK)) {
+ try {
+ mCurrentToken = mTransport.getCurrentRestoreSet();
+ } catch (RemoteException e) { /* cannot happen */ }
+ writeRestoreTokens();
+ }
+
// If things went wrong, we need to re-stage the apps we had expected
// to be backing up in this pass. This journals the package names in
// the current active pending-backup file, not in the we are holding
@@ -1392,6 +1446,7 @@
private IBackupTransport mTransport;
private IRestoreObserver mObserver;
private long mToken;
+ private PackageInfo mTargetPackage;
private File mStateDir;
class RestoreRequest {
@@ -1405,11 +1460,11 @@
}
PerformRestoreTask(IBackupTransport transport, IRestoreObserver observer,
- long restoreSetToken) {
+ long restoreSetToken, PackageInfo targetPackage) {
mTransport = transport;
- Log.d(TAG, "PerformRestoreThread mObserver=" + mObserver);
mObserver = observer;
mToken = restoreSetToken;
+ mTargetPackage = targetPackage;
try {
mStateDir = new File(mBaseStateDir, transport.transportDirName());
@@ -1421,7 +1476,8 @@
public void run() {
long startRealtime = SystemClock.elapsedRealtime();
if (DEBUG) Log.v(TAG, "Beginning restore process mTransport=" + mTransport
- + " mObserver=" + mObserver + " mToken=" + Long.toHexString(mToken));
+ + " mObserver=" + mObserver + " mToken=" + Long.toHexString(mToken)
+ + " mTargetPackage=" + mTargetPackage);
/**
* Restore sequence:
*
@@ -1438,7 +1494,7 @@
*
* On errors, we try our best to recover and move on to the next
* application, but if necessary we abort the whole operation --
- * the user is waiting, after al.
+ * the user is waiting, after all.
*/
int error = -1; // assume error
@@ -1456,7 +1512,12 @@
restorePackages.add(omPackage);
List<PackageInfo> agentPackages = allAgentPackages();
- restorePackages.addAll(agentPackages);
+ if (mTargetPackage == null) {
+ restorePackages.addAll(agentPackages);
+ } else {
+ // Just one package to attempt restore of
+ restorePackages.add(mTargetPackage);
+ }
// let the observer know that we're running
if (mObserver != null) {
@@ -1638,6 +1699,13 @@
}
}
+ // If this was a restoreAll operation, record that this was our
+ // ancestral dataset
+ if (mTargetPackage == null) {
+ mAncestralToken = mToken;
+ writeRestoreTokens();
+ }
+
// done; we can finally release the wakelock
mWakelock.release();
}
@@ -2019,6 +2087,20 @@
}
}
+ // Enable/disable automatic restore of app data at install time
+ public void setAutoRestore(boolean doAutoRestore) {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
+ "setBackupEnabled");
+
+ Log.i(TAG, "Auto restore => " + doAutoRestore);
+
+ synchronized (this) {
+ Settings.Secure.putInt(mContext.getContentResolver(),
+ Settings.Secure.BACKUP_AUTO_RESTORE, doAutoRestore ? 1 : 0);
+ mAutoRestore = doAutoRestore;
+ }
+ }
+
// Mark the backup service as having been provisioned
public void setBackupProvisioned(boolean available) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
@@ -2202,7 +2284,7 @@
}
}
- public synchronized int performRestore(long token, IRestoreObserver observer) {
+ public synchronized int restoreAll(long token, IRestoreObserver observer) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
"performRestore");
@@ -2232,6 +2314,55 @@
return -1;
}
+ public synchronized int restorePackage(String packageName, IRestoreObserver observer) {
+ if (DEBUG) Log.v(TAG, "restorePackage pkg=" + packageName + " obs=" + observer);
+
+ PackageInfo app = null;
+ try {
+ app = mPackageManager.getPackageInfo(packageName, 0);
+ } catch (NameNotFoundException nnf) {
+ Log.w(TAG, "Asked to restore nonexistent pkg " + packageName);
+ return -1;
+ }
+
+ // If the caller is not privileged and is not coming from the target
+ // app's uid, throw a permission exception back to the caller.
+ int perm = mContext.checkPermission(android.Manifest.permission.BACKUP,
+ Binder.getCallingPid(), Binder.getCallingUid());
+ if ((perm == PackageManager.PERMISSION_DENIED) &&
+ (app.applicationInfo.uid != Binder.getCallingUid())) {
+ Log.w(TAG, "restorePackage: bad packageName=" + packageName
+ + " or calling uid=" + Binder.getCallingUid());
+ throw new SecurityException("No permission to restore other packages");
+ }
+
+ // So far so good; we're allowed to try to restore this package. Now
+ // check whether there is data for it in the current dataset, falling back
+ // to the ancestral dataset if not.
+ long token = mAncestralToken;
+ synchronized (mQueueLock) {
+ if (mEverStoredApps.contains(packageName)) {
+ token = mCurrentToken;
+ }
+ }
+
+ // If we didn't come up with a place to look -- no ancestral dataset and
+ // the app has never been backed up from this device -- there's nothing
+ // to do but return failure.
+ if (token == 0) {
+ return -1;
+ }
+
+ // Ready to go: enqueue the restore request and claim success
+ long oldId = Binder.clearCallingIdentity();
+ mWakelock.acquire();
+ Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE);
+ msg.obj = new RestoreParams(mRestoreTransport, observer, token, app);
+ mBackupHandler.sendMessage(msg);
+ Binder.restoreCallingIdentity(oldId);
+ return 0;
+ }
+
public synchronized void endRestoreSession() {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
"endRestoreSession");
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index aa4956f..4259016 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -43,6 +43,8 @@
import com.android.internal.telephony.Phone;
+import com.android.server.connectivity.Tethering;
+
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -62,6 +64,9 @@
private static final String NETWORK_RESTORE_DELAY_PROP_NAME =
"android.telephony.apn-restore";
+
+ private Tethering mTethering;
+
/**
* Sometimes we want to refer to the individual network state
* trackers separately, and sometimes we just want to treat them
@@ -308,6 +313,8 @@
continue;
}
}
+
+ mTethering = new Tethering(mContext);
}
@@ -784,6 +791,13 @@
"ConnectivityService");
}
+ // TODO Make this a special check when it goes public
+ private void enforceTetherChangePermission() {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.CHANGE_NETWORK_STATE,
+ "ConnectivityService");
+ }
+
/**
* Handle a {@code DISCONNECTED} event. If this pertains to the non-active
* network, we ignore it. If it is for the active network, we send out a
@@ -1368,4 +1382,28 @@
}
}
}
+
+ // javadoc from interface
+ public boolean tether(String iface) {
+ enforceTetherChangePermission();
+ return mTethering.tether(iface);
+ }
+
+ // javadoc from interface
+ public boolean untether(String iface) {
+ enforceTetherChangePermission();
+ return mTethering.untether(iface);
+ }
+
+ // TODO - move iface listing, queries, etc to new module
+ // javadoc from interface
+ public String[] getTetherableIfaces() {
+ enforceAccessPermission();
+ return mTethering.getTetherableIfaces();
+ }
+
+ public String[] getTetheredIfaces() {
+ enforceAccessPermission();
+ return mTethering.getTetheredIfaces();
+ }
}
diff --git a/services/java/com/android/server/HeadsetObserver.java b/services/java/com/android/server/HeadsetObserver.java
index 9d69564..c94450b 100644
--- a/services/java/com/android/server/HeadsetObserver.java
+++ b/services/java/com/android/server/HeadsetObserver.java
@@ -53,6 +53,19 @@
private final Context mContext;
private final WakeLock mWakeLock; // held while there is a pending route change
+ private boolean mHandleTTY;
+ private int mTTYState;
+ private AudioManager mAudioManager = null;
+
+ // special use of bits in headset state received from kernel made by some
+ // platforms to indicate changes in TTY mode.
+ private static final int BIT_TTY_OFF = 0;
+ private static final int BIT_TTY_FULL = (1 << 2);
+ private static final int BIT_TTY_VCO = (1 << 5);
+ private static final int BIT_TTY_HCO = (1 << 6);
+ private static final int TTY_BITS_MASK = (BIT_TTY_FULL | BIT_TTY_VCO | BIT_TTY_HCO);
+
+
public HeadsetObserver(Context context) {
mContext = context;
PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
@@ -61,6 +74,11 @@
startObserving(HEADSET_UEVENT_MATCH);
+ // read settings for TTY mode indication method
+ mHandleTTY = context.getResources().getBoolean(
+ com.android.internal.R.bool.tty_mode_uses_headset_events);
+ mTTYState = BIT_TTY_OFF;
+
init(); // set initial status
}
@@ -100,6 +118,39 @@
}
private synchronized final void update(String newName, int newState) {
+ // handle TTY state change first
+ if (mHandleTTY) {
+ int ttyState = newState & TTY_BITS_MASK;
+ if (ttyState != mTTYState) {
+ String ttyMode;
+
+ switch (ttyState) {
+ case BIT_TTY_FULL:
+ ttyMode = "tty_full";
+ break;
+ case BIT_TTY_VCO:
+ ttyMode = "tty_vco";
+ break;
+ case BIT_TTY_HCO:
+ ttyMode = "tty_hco";
+ break;
+ case BIT_TTY_OFF:
+ ttyMode = "tty_off";
+ break;
+ default:
+ ttyMode = "tty_invalid";
+ break;
+
+ }
+ if (ttyMode != "tty_invalid") {
+ mTTYState = ttyState;
+ if (mAudioManager == null) {
+ mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
+ }
+ mAudioManager.setParameters("tty_mode="+ttyMode);
+ }
+ }
+ }
// Retain only relevant bits
int headsetState = newState & SUPPORTED_HEADSETS;
int newOrOld = headsetState | mHeadsetState;
diff --git a/services/java/com/android/server/Installer.java b/services/java/com/android/server/Installer.java
index 88b1f02..11297d5 100644
--- a/services/java/com/android/server/Installer.java
+++ b/services/java/com/android/server/Installer.java
@@ -222,6 +222,21 @@
return execute(builder.toString());
}
+ public int rename(String oldname, String newname, boolean useEncryptedFilesystem) {
+ StringBuilder builder = new StringBuilder("rename");
+ builder.append(' ');
+ builder.append(oldname);
+ builder.append(' ');
+ builder.append(newname);
+ builder.append(' ');
+ if (useEncryptedFilesystem) {
+ builder.append('1');
+ } else {
+ builder.append('0');
+ }
+ return execute(builder.toString());
+ }
+
public int deleteCacheFiles(String name, boolean useEncryptedFilesystem) {
StringBuilder builder = new StringBuilder("rmcache");
builder.append(' ');
@@ -308,4 +323,8 @@
return -1;
}
}
+
+ public int moveFiles() {
+ return execute("movefiles");
+ }
}
diff --git a/services/java/com/android/server/NativeDaemonConnector.java b/services/java/com/android/server/NativeDaemonConnector.java
index 016aa52..9a066d3 100644
--- a/services/java/com/android/server/NativeDaemonConnector.java
+++ b/services/java/com/android/server/NativeDaemonConnector.java
@@ -219,19 +219,23 @@
String.format("Invalid response from daemon (%s)", line));
}
- if ((code >= 200) && (code < 600))
+ if ((code >= 200) && (code < 600)) {
complete = true;
+ }
response.add(line);
} catch (InterruptedException ex) {
- Log.e(TAG, "InterruptedException");
+ Log.e(TAG, "Failed to process response", ex);
}
}
if (code >= ResponseCode.FailedRangeStart &&
code <= ResponseCode.FailedRangeEnd) {
- throw new NativeDaemonConnectorException(code, String.format(
- "Command %s failed with code %d",
- cmd, code));
+ /*
+ * Note: The format of the last response in this case is
+ * "NNN <errmsg>"
+ */
+ throw new NativeDaemonConnectorException(
+ code, cmd, response.get(response.size()-1).substring(4));
}
return response;
}
diff --git a/services/java/com/android/server/NativeDaemonConnectorException.java b/services/java/com/android/server/NativeDaemonConnectorException.java
index e60aaf8..426742b 100644
--- a/services/java/com/android/server/NativeDaemonConnectorException.java
+++ b/services/java/com/android/server/NativeDaemonConnectorException.java
@@ -22,6 +22,7 @@
public class NativeDaemonConnectorException extends RuntimeException
{
private int mCode = -1;
+ private String mCmd;
public NativeDaemonConnectorException() {}
@@ -30,13 +31,18 @@
super(error);
}
- public NativeDaemonConnectorException(int code, String error)
+ public NativeDaemonConnectorException(int code, String cmd, String error)
{
- super(error);
+ super(String.format("Cmd {%s} failed with code %d : {%s}", cmd, code, error));
mCode = code;
+ mCmd = cmd;
}
public int getCode() {
return mCode;
}
+
+ public String getCmd() {
+ return mCmd;
+ }
}
diff --git a/services/java/com/android/server/NetStatService.java b/services/java/com/android/server/NetStatService.java
index 0834405..ac3445e 100644
--- a/services/java/com/android/server/NetStatService.java
+++ b/services/java/com/android/server/NetStatService.java
@@ -27,11 +27,11 @@
}
public long getMobileTxPackets() {
- return TrafficStats.getMobileTxPkts();
+ return TrafficStats.getMobileTxPackets();
}
public long getMobileRxPackets() {
- return TrafficStats.getMobileRxPkts();
+ return TrafficStats.getMobileRxPackets();
}
public long getMobileTxBytes() {
@@ -43,11 +43,11 @@
}
public long getTotalTxPackets() {
- return TrafficStats.getTotalTxPkts();
+ return TrafficStats.getTotalTxPackets();
}
public long getTotalRxPackets() {
- return TrafficStats.getTotalRxPkts();
+ return TrafficStats.getTotalRxPackets();
}
public long getTotalTxBytes() {
diff --git a/services/java/com/android/server/NetworkManagementService.java b/services/java/com/android/server/NetworkManagementService.java
index b34b50a..d41aacf 100644
--- a/services/java/com/android/server/NetworkManagementService.java
+++ b/services/java/com/android/server/NetworkManagementService.java
@@ -334,9 +334,9 @@
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
try {
- String cmd = "tether dns set ";
+ String cmd = "tether dns set";
for (String s : dns) {
- cmd += InetAddress.getByName(s).toString() + " ";
+ cmd += " " + InetAddress.getByName(s).getHostAddress();
}
mConnector.doCommand(cmd);
} catch (UnknownHostException e) {
@@ -373,14 +373,16 @@
return mConnector.doListCommand("list_ttys", NetdResponseCode.TtyListResult);
}
- public void attachPppd(String tty, String localAddr, String remoteAddr)
- throws IllegalStateException {
+ public void attachPppd(String tty, String localAddr, String remoteAddr, String dns1Addr,
+ String dns2Addr) throws IllegalStateException {
try {
mContext.enforceCallingOrSelfPermission(
android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService");
- mConnector.doCommand(String.format("pppd attach %s %s %s", tty,
- InetAddress.getByName(localAddr).toString(),
- InetAddress.getByName(localAddr).toString()));
+ mConnector.doCommand(String.format("pppd attach %s %s %s %s %s", tty,
+ InetAddress.getByName(localAddr).getHostAddress(),
+ InetAddress.getByName(remoteAddr).getHostAddress(),
+ InetAddress.getByName(dns1Addr).getHostAddress(),
+ InetAddress.getByName(dns2Addr).getHostAddress()));
} catch (UnknownHostException e) {
throw new IllegalStateException("Error resolving addr", e);
}
@@ -392,4 +394,3 @@
mConnector.doCommand(String.format("pppd detach %s", tty));
}
}
-
diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java
index 238403c..387f139 100644
--- a/services/java/com/android/server/PackageManagerService.java
+++ b/services/java/com/android/server/PackageManagerService.java
@@ -96,11 +96,13 @@
import java.io.InputStream;
import java.io.PrintWriter;
import java.security.NoSuchAlgorithmException;
+import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
+import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
@@ -117,6 +119,7 @@
private static final String TAG = "PackageManager";
private static final boolean DEBUG_SETTINGS = false;
private static final boolean DEBUG_PREFERRED = false;
+ private static final boolean DEBUG_UPGRADE = false;
private static final boolean MULTIPLE_APPLICATION_UIDS = true;
private static final int RADIO_UID = Process.PHONE_UID;
@@ -221,7 +224,6 @@
final Settings mSettings;
boolean mRestoredSettings;
- boolean mReportedUidError;
// Group-ids that are given to all packages as read from etc/permissions/*.xml.
int[] mGlobalGids;
@@ -271,6 +273,10 @@
final HashMap<String, PackageParser.PermissionGroup> mPermissionGroups =
new HashMap<String, PackageParser.PermissionGroup>();
+ // Packages whose data we have transfered into another package, thus
+ // should no longer exist.
+ final HashSet<String> mTransferedPackages = new HashSet<String>();
+
// Broadcast actions that are only available to the system.
final HashSet<String> mProtectedBroadcasts = new HashSet<String>();
@@ -661,16 +667,43 @@
}
}
+ // Find base frameworks (resource packages without code).
mFrameworkInstallObserver = new AppDirObserver(
mFrameworkDir.getPath(), OBSERVER_EVENTS, true);
mFrameworkInstallObserver.startWatching();
scanDirLI(mFrameworkDir, PackageParser.PARSE_IS_SYSTEM,
scanMode | SCAN_NO_DEX);
+
+ // Collect all system packages.
mSystemAppDir = new File(Environment.getRootDirectory(), "app");
mSystemInstallObserver = new AppDirObserver(
mSystemAppDir.getPath(), OBSERVER_EVENTS, true);
mSystemInstallObserver.startWatching();
scanDirLI(mSystemAppDir, PackageParser.PARSE_IS_SYSTEM, scanMode);
+
+ if (mInstaller != null) {
+ if (DEBUG_UPGRADE) Log.v(TAG, "Running installd update commands");
+ mInstaller.moveFiles();
+ }
+
+ // Prune any system packages that no longer exist.
+ Iterator<PackageSetting> psit = mSettings.mPackages.values().iterator();
+ while (psit.hasNext()) {
+ PackageSetting ps = psit.next();
+ if ((ps.pkgFlags&ApplicationInfo.FLAG_SYSTEM) != 0
+ && !mPackages.containsKey(ps.name)) {
+ psit.remove();
+ String msg = "System package " + ps.name
+ + " no longer exists; wiping its data";
+ reportSettingsProblem(Log.WARN, msg);
+ if (mInstaller != null) {
+ // XXX how to set useEncryptedFSDir for packages that
+ // are not encrypted?
+ mInstaller.remove(ps.name, true);
+ }
+ }
+ }
+
mAppInstallDir = new File(dataDir, "app");
if (mInstaller == null) {
// Make sure these dirs exist, when we are running in
@@ -2081,14 +2114,21 @@
}
}
+ private static File getSettingsProblemFile() {
+ File dataDir = Environment.getDataDirectory();
+ File systemDir = new File(dataDir, "system");
+ File fname = new File(systemDir, "uiderrors.txt");
+ return fname;
+ }
+
private static void reportSettingsProblem(int priority, String msg) {
try {
- File dataDir = Environment.getDataDirectory();
- File systemDir = new File(dataDir, "system");
- File fname = new File(systemDir, "uiderrors.txt");
+ File fname = getSettingsProblemFile();
FileOutputStream out = new FileOutputStream(fname, true);
PrintWriter pw = new PrintWriter(out);
- pw.println(msg);
+ SimpleDateFormat formatter = new SimpleDateFormat();
+ String dateString = formatter.format(new Date(System.currentTimeMillis()));
+ pw.println(dateString + ": " + msg);
pw.close();
FileUtils.setPermissions(
fname.toString(),
@@ -2295,6 +2335,21 @@
((pkg.applicationInfo.flags & ApplicationInfo.FLAG_NEVER_ENCRYPT) == 0);
}
+ private boolean verifyPackageUpdate(PackageSetting oldPkg, PackageParser.Package newPkg) {
+ if ((oldPkg.pkgFlags&ApplicationInfo.FLAG_SYSTEM) == 0) {
+ Log.w(TAG, "Unable to update from " + oldPkg.name
+ + " to " + newPkg.packageName
+ + ": old package not in system partition");
+ return false;
+ } else if (mPackages.get(oldPkg.name) != null) {
+ Log.w(TAG, "Unable to update from " + oldPkg.name
+ + " to " + newPkg.packageName
+ + ": old package still exists");
+ return false;
+ }
+ return true;
+ }
+
private PackageParser.Package scanPackageLI(
PackageParser.Package pkg, int parseFlags, int scanMode) {
File scanFile = new File(pkg.mScanPath);
@@ -2439,22 +2494,67 @@
}
}
+ // Check if we are renaming from an original package name.
+ PackageSetting origPackage = null;
+ if (pkg.mOriginalPackage != null) {
+ // We will only retrieve the setting for it if it already
+ // exists; otherwise we need to make a new one later.
+ origPackage = mSettings.peekPackageLP(pkg.mOriginalPackage);
+ if (origPackage != null) {
+ if (!verifyPackageUpdate(origPackage, pkg)) {
+ origPackage = null;
+ } else if (origPackage.sharedUser != null) {
+ if (!origPackage.sharedUser.name.equals(pkg.mSharedUserId)) {
+ Log.w(TAG, "Unable to migrate data from " + origPackage.name
+ + " to " + pkg.packageName + ": old uid "
+ + origPackage.sharedUser.name
+ + " differs from " + pkg.mSharedUserId);
+ origPackage = null;
+ }
+ } else {
+ if (DEBUG_UPGRADE) Log.v(TAG, "Migrating data from "
+ + origPackage.name + " to " + pkg.packageName);
+ }
+ }
+ }
+
+ if (mTransferedPackages.contains(pkg.packageName)) {
+ Log.w(TAG, "Package " + pkg.packageName
+ + " was transferred to another, but its .apk remains");
+ }
+
// Just create the setting, don't add it yet. For already existing packages
// the PkgSetting exists already and doesn't have to be created.
- pkgSetting = mSettings.getPackageLP(pkg, suid, destCodeFile,
+ pkgSetting = mSettings.getPackageLP(pkg, origPackage, suid, destCodeFile,
destResourceFile, pkg.applicationInfo.flags, true, false);
if (pkgSetting == null) {
Log.w(TAG, "Creating application package " + pkgName + " failed");
mLastScanError = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
return null;
}
- if(mSettings.mDisabledSysPackages.get(pkg.packageName) != null) {
+ if (mSettings.mDisabledSysPackages.get(pkg.packageName) != null) {
pkg.applicationInfo.flags |= ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
}
pkg.applicationInfo.uid = pkgSetting.userId;
pkg.mExtras = pkgSetting;
+ if (pkg.mAdoptPermissions != null) {
+ // This package wants to adopt ownership of permissions from
+ // another package.
+ for (int i=pkg.mAdoptPermissions.size()-1; i>=0; i--) {
+ String origName = pkg.mAdoptPermissions.get(i);
+ PackageSetting orig = mSettings.peekPackageLP(origName);
+ if (orig != null) {
+ if (verifyPackageUpdate(orig, pkg)) {
+ if (DEBUG_UPGRADE) Log.v(TAG, "Adopting permissions from "
+ + origName + " to " + pkg.packageName);
+ mSettings.transferPermissions(origName, pkg.packageName);
+ }
+ }
+ }
+ }
+
if (!verifySignaturesLP(pkgSetting, pkg, parseFlags,
(scanMode&SCAN_UPDATE_SIGNATURE) != 0)) {
if ((parseFlags&PackageParser.PARSE_IS_SYSTEM) == 0) {
@@ -2543,6 +2643,9 @@
} else {
dataPath = new File(mAppDataDir, pkgName);
}
+
+ boolean uidError = false;
+
if (dataPath.exists()) {
mOutPermissions[1] = 0;
FileUtils.getPermissions(dataPath.getPath(), mOutPermissions);
@@ -2556,7 +2659,7 @@
// current data so the application will still work.
if (mInstaller != null) {
int ret = mInstaller.remove(pkgName, useEncryptedFSDir);
- if(ret >= 0) {
+ if (ret >= 0) {
// Old data gone!
String msg = "System package " + pkg.packageName
+ " has changed from uid: "
@@ -2591,12 +2694,12 @@
+ mOutPermissions[1] + " on disk, "
+ pkg.applicationInfo.uid + " in settings";
synchronized (mPackages) {
- if (!mReportedUidError) {
- mReportedUidError = true;
- msg = msg + "; read messages:\n"
- + mSettings.getReadMessagesLP();
+ mSettings.mReadMessages.append(msg);
+ mSettings.mReadMessages.append('\n');
+ uidError = true;
+ if (!pkgSetting.uidError) {
+ reportSettingsProblem(Log.ERROR, msg);
}
- reportSettingsProblem(Log.ERROR, msg);
}
}
}
@@ -2604,6 +2707,29 @@
} else {
if ((parseFlags&PackageParser.PARSE_CHATTY) != 0 && Config.LOGV)
Log.v(TAG, "Want this data dir: " + dataPath);
+ if (pkgSetting.origPackage != null) {
+ synchronized (mPackages) {
+ // This package is being update from another; rename the
+ // old one's data dir.
+ String msg = "Transfering data from old package "
+ + pkgSetting.origPackage.name + " to new package "
+ + pkgSetting.name;
+ reportSettingsProblem(Log.WARN, msg);
+ if (mInstaller != null) {
+ int ret = mInstaller.rename(pkgSetting.origPackage.name,
+ pkgName, useEncryptedFSDir);
+ if(ret < 0) {
+ msg = "Error transfering data from old package "
+ + pkgSetting.origPackage.name + " to new package "
+ + pkgSetting.name;
+ reportSettingsProblem(Log.WARN, msg);
+ }
+ }
+ // And now uninstall the old package.
+ mInstaller.remove(pkgSetting.origPackage.name, useEncryptedFSDir);
+ mSettings.removePackageLP(pkgSetting.origPackage.name);
+ }
+ }
//invoke installer to do the actual installation
if (mInstaller != null) {
int ret = mInstaller.install(pkgName, useEncryptedFSDir, pkg.applicationInfo.uid,
@@ -2629,8 +2755,16 @@
pkg.applicationInfo.dataDir = null;
}
}
+
+ pkgSetting.uidError = uidError;
}
+ // No longer need to retain this.
+ if (pkgSetting.origPackage != null) {
+ mTransferedPackages.add(pkgSetting.origPackage.name);
+ pkgSetting.origPackage = null;
+ }
+
// Perform shared library installation and dex validation and
// optimization, if this is not a system app.
if (mInstaller != null) {
@@ -5951,16 +6085,31 @@
}
}
}
+
pw.println(" ");
pw.println("Settings parse messages:");
pw.println(mSettings.mReadMessages.toString());
+
+ pw.println(" ");
+ pw.println("Package warning messages:");
+ File fname = getSettingsProblemFile();
+ FileInputStream in;
+ try {
+ in = new FileInputStream(fname);
+ int avail = in.available();
+ byte[] data = new byte[avail];
+ in.read(data);
+ pw.println(new String(data));
+ } catch (FileNotFoundException e) {
+ } catch (IOException e) {
+ }
}
synchronized (mProviders) {
pw.println(" ");
pw.println("Registered ContentProviders:");
for (PackageParser.Provider p : mProviders.values()) {
- pw.println(" ["); pw.println(p.info.authority); pw.println("]: ");
+ pw.print(" ["); pw.print(p.info.authority); pw.print("]: ");
pw.println(p.toString());
}
}
@@ -5972,7 +6121,7 @@
final static int TYPE_DYNAMIC = 2;
final String name;
- final String sourcePackage;
+ String sourcePackage;
final int type;
PackageParser.Permission perm;
PermissionInfo pendingInfo;
@@ -6437,6 +6586,8 @@
private String timeStampString = "0";
int versionCode;
+ boolean uidError;
+
PackageSignatures signatures = new PackageSignatures();
boolean permissionsFixed;
@@ -6448,6 +6599,8 @@
int enabled = COMPONENT_ENABLED_STATE_DEFAULT;
int installStatus = PKG_INSTALL_COMPLETE;
+ PackageSettingBase origPackage;
+
/* package name of the app that installed this package */
String installerPackageName;
@@ -6455,13 +6608,17 @@
int pVersionCode, int pkgFlags) {
super(pkgFlags);
this.name = name;
+ init(codePath, resourcePath, pVersionCode);
+ }
+
+ void init(File codePath, File resourcePath, int pVersionCode) {
this.codePath = codePath;
this.codePathString = codePath.toString();
this.resourcePath = resourcePath;
this.resourcePathString = resourcePath.toString();
this.versionCode = pVersionCode;
}
-
+
public void setInstallerPackageName(String packageName) {
installerPackageName = packageName;
}
@@ -6667,11 +6824,11 @@
mBackupSettingsFilename = new File(systemDir, "packages-backup.xml");
}
- PackageSetting getPackageLP(PackageParser.Package pkg,
+ PackageSetting getPackageLP(PackageParser.Package pkg, PackageSetting origPackage,
SharedUserSetting sharedUser, File codePath, File resourcePath,
int pkgFlags, boolean create, boolean add) {
final String name = pkg.packageName;
- PackageSetting p = getPackageLP(name, sharedUser, codePath,
+ PackageSetting p = getPackageLP(name, origPackage, sharedUser, codePath,
resourcePath, pkg.mVersionCode, pkgFlags, create, add);
return p;
}
@@ -6813,7 +6970,31 @@
return null;
}
- private PackageSetting getPackageLP(String name,
+ // Transfer ownership of permissions from one package to another.
+ private void transferPermissions(String origPkg, String newPkg) {
+ // Transfer ownership of permissions to the new package.
+ for (int i=0; i<2; i++) {
+ HashMap<String, BasePermission> permissions =
+ i == 0 ? mPermissionTrees : mPermissions;
+ for (BasePermission bp : permissions.values()) {
+ if (origPkg.equals(bp.sourcePackage)) {
+ if (DEBUG_UPGRADE) Log.v(TAG,
+ "Moving permission " + bp.name
+ + " from pkg " + bp.sourcePackage
+ + " to " + newPkg);
+ bp.sourcePackage = newPkg;
+ bp.perm = null;
+ if (bp.pendingInfo != null) {
+ bp.sourcePackage = newPkg;
+ }
+ bp.uid = 0;
+ bp.gids = null;
+ }
+ }
+ }
+ }
+
+ private PackageSetting getPackageLP(String name, PackageSetting origPackage,
SharedUserSetting sharedUser, File codePath, File resourcePath,
int vc, int pkgFlags, boolean create, boolean add) {
PackageSetting p = mPackages.get(name);
@@ -6857,36 +7038,49 @@
return null;
}
p = new PackageSetting(name, codePath, resourcePath, vc, pkgFlags);
- p.setTimeStamp(codePath.lastModified());
- p.sharedUser = sharedUser;
- if (sharedUser != null) {
- p.userId = sharedUser.userId;
- } else if (MULTIPLE_APPLICATION_UIDS) {
- // Clone the setting here for disabled system packages
- PackageSetting dis = mDisabledSysPackages.get(name);
- if (dis != null) {
- // For disabled packages a new setting is created
- // from the existing user id. This still has to be
- // added to list of user id's
- // Copy signatures from previous setting
- if (dis.signatures.mSignatures != null) {
- p.signatures.mSignatures = dis.signatures.mSignatures.clone();
- }
- p.userId = dis.userId;
- // Clone permissions
- p.grantedPermissions = new HashSet<String>(dis.grantedPermissions);
- p.loadedPermissions = new HashSet<String>(dis.loadedPermissions);
- // Clone component info
- p.disabledComponents = new HashSet<String>(dis.disabledComponents);
- p.enabledComponents = new HashSet<String>(dis.enabledComponents);
- // Add new setting to list of user ids
- addUserIdLP(p.userId, p, name);
- } else {
- // Assign new user id
- p.userId = newUserIdLP(p);
- }
+ if (origPackage != null) {
+ // We are consuming the data from an existing package.
+ if (DEBUG_UPGRADE) Log.v(TAG, "Package " + name
+ + " is adopting original package " + origPackage.name);
+ p.copyFrom(origPackage);
+ p.sharedUser = origPackage.sharedUser;
+ p.userId = origPackage.userId;
+ p.origPackage = origPackage;
+ transferPermissions(origPackage.name, name);
+ // Update new package state.
+ p.setTimeStamp(codePath.lastModified());
} else {
- p.userId = FIRST_APPLICATION_UID;
+ p.setTimeStamp(codePath.lastModified());
+ p.sharedUser = sharedUser;
+ if (sharedUser != null) {
+ p.userId = sharedUser.userId;
+ } else if (MULTIPLE_APPLICATION_UIDS) {
+ // Clone the setting here for disabled system packages
+ PackageSetting dis = mDisabledSysPackages.get(name);
+ if (dis != null) {
+ // For disabled packages a new setting is created
+ // from the existing user id. This still has to be
+ // added to list of user id's
+ // Copy signatures from previous setting
+ if (dis.signatures.mSignatures != null) {
+ p.signatures.mSignatures = dis.signatures.mSignatures.clone();
+ }
+ p.userId = dis.userId;
+ // Clone permissions
+ p.grantedPermissions = new HashSet<String>(dis.grantedPermissions);
+ p.loadedPermissions = new HashSet<String>(dis.loadedPermissions);
+ // Clone component info
+ p.disabledComponents = new HashSet<String>(dis.disabledComponents);
+ p.enabledComponents = new HashSet<String>(dis.enabledComponents);
+ // Add new setting to list of user ids
+ addUserIdLP(p.userId, p, name);
+ } else {
+ // Assign new user id
+ p.userId = newUserIdLP(p);
+ }
+ } else {
+ p.userId = FIRST_APPLICATION_UID;
+ }
}
if (p.userId < 0) {
reportSettingsProblem(Log.WARN,
@@ -7248,6 +7442,9 @@
serializer.attribute(null, "sharedUserId",
Integer.toString(pkg.userId));
}
+ if (pkg.uidError) {
+ serializer.attribute(null, "uidError", "true");
+ }
if (pkg.enabled != COMPONENT_ENABLED_STATE_DEFAULT) {
serializer.attribute(null, "enabled",
pkg.enabled == COMPONENT_ENABLED_STATE_ENABLED
@@ -7441,7 +7638,7 @@
final PendingPackage pp = mPendingPackages.get(i);
Object idObj = getUserIdLP(pp.sharedId);
if (idObj != null && idObj instanceof SharedUserSetting) {
- PackageSetting p = getPackageLP(pp.name,
+ PackageSetting p = getPackageLP(pp.name, null,
(SharedUserSetting)idObj, pp.codePath, pp.resourcePath,
pp.versionCode, pp.pkgFlags, true, true);
if (p == null) {
@@ -7610,6 +7807,7 @@
String resourcePathStr = null;
String systemStr = null;
String installerPackageName = null;
+ String uidError = null;
int pkgFlags = 0;
String timeStampStr;
long timeStamp = 0;
@@ -7619,6 +7817,7 @@
try {
name = parser.getAttributeValue(null, "name");
idStr = parser.getAttributeValue(null, "userId");
+ uidError = parser.getAttributeValue(null, "uidError");
sharedIdStr = parser.getAttributeValue(null, "sharedUserId");
codePathStr = parser.getAttributeValue(null, "codePath");
resourcePathStr = parser.getAttributeValue(null, "resourcePath");
@@ -7712,6 +7911,7 @@
+ parser.getPositionDescription());
}
if (packageSetting != null) {
+ packageSetting.uidError = "true".equals(uidError);
packageSetting.installerPackageName = installerPackageName;
final String enabledStr = parser.getAttributeValue(null, "enabled");
if (enabledStr != null) {
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 22447ed..ee1cc8d 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -529,7 +529,8 @@
// The system server has to run all of the time, so it needs to be
// as efficient as possible with its memory usage.
VMRuntime.getRuntime().setTargetHeapUtilization(0.8f);
-
+ VMRuntime.getRuntime().startJitCompilation();
+
System.loadLibrary("android_servers");
init1(args);
}
diff --git a/services/java/com/android/server/connectivity/Tethering.java b/services/java/com/android/server/connectivity/Tethering.java
new file mode 100644
index 0000000..f685383
--- /dev/null
+++ b/services/java/com/android/server/connectivity/Tethering.java
@@ -0,0 +1,483 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.connectivity;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.res.Resources;
+import android.net.ConnectivityManager;
+import android.net.INetworkManagementEventObserver;
+import android.os.IBinder;
+import android.os.INetworkManagementService;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.provider.Settings;
+import android.util.Log;
+
+import java.util.ArrayList;
+/**
+ * @hide
+ */
+public class Tethering extends INetworkManagementEventObserver.Stub {
+
+ private Notification mTetheringNotification;
+ private Context mContext;
+ private final String TAG = "Tethering";
+
+ private boolean mPlaySounds = false;
+
+ private ArrayList<String> mAvailableIfaces;
+ private ArrayList<String> mActiveIfaces;
+
+ private ArrayList<String> mActiveTtys;
+
+ private BroadcastReceiver mStateReceiver;
+
+ public Tethering(Context context) {
+ Log.d(TAG, "Tethering starting");
+ mContext = context;
+
+ // register for notifications from NetworkManagement Service
+ IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
+ INetworkManagementService service = INetworkManagementService.Stub.asInterface(b);
+ try {
+ service.registerObserver(this);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error registering observer :" + e);
+ }
+
+ mAvailableIfaces = new ArrayList<String>();
+ mActiveIfaces = new ArrayList<String>();
+ mActiveTtys = new ArrayList<String>();
+
+ // TODO - remove this hack after real USB connections are detected.
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(Intent.ACTION_UMS_DISCONNECTED);
+ filter.addAction(Intent.ACTION_UMS_CONNECTED);
+ mStateReceiver = new UMSStateReceiver();
+ mContext.registerReceiver(mStateReceiver, filter);
+ }
+
+ public synchronized void interfaceLinkStatusChanged(String iface, boolean link) {
+ Log.d(TAG, "interfaceLinkStatusChanged " + iface + ", " + link);
+ }
+
+ public synchronized void interfaceAdded(String iface) {
+ if (mActiveIfaces.contains(iface)) {
+ Log.e(TAG, "active iface (" + iface + ") reported as added, ignoring");
+ return;
+ }
+ if (mAvailableIfaces.contains(iface)) {
+ Log.e(TAG, "available iface (" + iface + ") readded, ignoring");
+ return;
+ }
+ mAvailableIfaces.add(iface);
+ Log.d(TAG, "interfaceAdded :" + iface);
+ sendTetherStateChangedBroadcast();
+ }
+
+ public synchronized void interfaceRemoved(String iface) {
+ if (mActiveIfaces.contains(iface)) {
+ Log.d(TAG, "removed an active iface (" + iface + ")");
+ untether(iface);
+ }
+ if (mAvailableIfaces.contains(iface)) {
+ mAvailableIfaces.remove(iface);
+ Log.d(TAG, "interfaceRemoved " + iface);
+ sendTetherStateChangedBroadcast();
+ }
+ }
+
+ public synchronized boolean tether(String iface) {
+ Log.d(TAG, "Tethering " + iface);
+
+ if (!mAvailableIfaces.contains(iface)) {
+ Log.e(TAG, "Tried to Tether an unavailable iface :" + iface + ", ignoring");
+ return false;
+ }
+ if (mActiveIfaces.contains(iface)) {
+ Log.e(TAG, "Tried to Tether an already Tethered iface :" + iface + ", ignoring");
+ return false;
+ }
+
+ IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
+ INetworkManagementService service = INetworkManagementService.Stub.asInterface(b);
+
+ if (mActiveIfaces.size() == 0) {
+ try {
+ service.setIpForwardingEnabled(true);
+ } catch (Exception e) {
+ Log.e(TAG, "Error in setIpForwardingEnabled(true) :" + e);
+ return false;
+ }
+
+ try {
+ // TODO - don't hardcode this - though with non-conf values (un-routable)
+ // maybe it's not a big deal
+ service.startTethering("169.254.2.1", "169.254.2.64");
+ } catch (Exception e) {
+ Log.e(TAG, "Error in startTethering :" + e);
+ try {
+ service.setIpForwardingEnabled(false);
+ } catch (Exception ee) {}
+ return false;
+ }
+
+ try {
+ // TODO - maybe use the current connection's dns servers for this
+ String[] dns = new String[2];
+ dns[0] = new String("8.8.8.8");
+ dns[1] = new String("4.2.2.2");
+ service.setDnsForwarders(dns);
+ } catch (Exception e) {
+ Log.e(TAG, "Error in setDnsForwarders :" + e);
+ try {
+ service.stopTethering();
+ } catch (Exception ee) {}
+ try {
+ service.setIpForwardingEnabled(false);
+ } catch (Exception ee) {}
+ }
+ }
+
+ try {
+ service.tetherInterface(iface);
+ } catch (Exception e) {
+ Log.e(TAG, "Error in tetherInterface :" + e);
+ if (mActiveIfaces.size() == 0) {
+ try {
+ service.stopTethering();
+ } catch (Exception ee) {}
+ try {
+ service.setIpForwardingEnabled(false);
+ } catch (Exception ee) {}
+ }
+ return false;
+ }
+
+ try {
+ // TODO - use the currently active external iface
+ service.enableNat (iface, "rmnet0");
+ } catch (Exception e) {
+ Log.e(TAG, "Error in enableNat :" + e);
+ try {
+ service.untetherInterface(iface);
+ } catch (Exception ee) {}
+ if (mActiveIfaces.size() == 0) {
+ try {
+ service.stopTethering();
+ } catch (Exception ee) {}
+ try {
+ service.setIpForwardingEnabled(false);
+ } catch (Exception ee) {}
+ }
+ return false;
+ }
+ mAvailableIfaces.remove(iface);
+ mActiveIfaces.add(iface);
+ Log.d(TAG, "Tethered " + iface);
+ sendTetherStateChangedBroadcast();
+ return true;
+ }
+
+ public synchronized boolean untether(String iface) {
+ Log.d(TAG, "Untethering " + iface);
+
+ if (mAvailableIfaces.contains(iface)) {
+ Log.e(TAG, "Tried to Untether an available iface :" + iface);
+ return false;
+ }
+ if (!mActiveIfaces.contains(iface)) {
+ Log.e(TAG, "Tried to Untether an inactive iface :" + iface);
+ return false;
+ }
+
+ IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
+ INetworkManagementService service = INetworkManagementService.Stub.asInterface(b);
+
+ // none of these errors are recoverable - ie, multiple calls won't help
+ // and the user can't do anything. Basically a reboot is required and probably
+ // the device is misconfigured or something bad has happend.
+ // Because of this, we should try to unroll as much state as we can.
+ try {
+ service.disableNat(iface, "rmnet0");
+ } catch (Exception e) {
+ Log.e(TAG, "Error in disableNat :" + e);
+ }
+ try {
+ service.untetherInterface(iface);
+ } catch (Exception e) {
+ Log.e(TAG, "Error untethering " + iface + ", :" + e);
+ }
+ mActiveIfaces.remove(iface);
+ mAvailableIfaces.add(iface);
+
+ if (mActiveIfaces.size() == 0) {
+ Log.d(TAG, "no active tethers - turning down dhcp/ipforward");
+ try {
+ service.stopTethering();
+ } catch (Exception e) {
+ Log.e(TAG, "Error in stopTethering :" + e);
+ }
+ try {
+ service.setIpForwardingEnabled(false);
+ } catch (Exception e) {
+ Log.e(TAG, "Error in setIpForwardingEnabled(false) :" + e);
+ }
+ }
+ sendTetherStateChangedBroadcast();
+ Log.d(TAG, "Untethered " + iface);
+ return true;
+ }
+
+ private void sendTetherStateChangedBroadcast() {
+ Intent broadcast = new Intent(ConnectivityManager.ACTION_TETHER_STATE_CHANGED);
+ broadcast.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
+ broadcast.putExtra(ConnectivityManager.EXTRA_AVAILABLE_TETHER_COUNT,
+ mAvailableIfaces.size());
+ broadcast.putExtra(ConnectivityManager.EXTRA_ACTIVE_TETHER_COUNT, mActiveIfaces.size());
+ mContext.sendBroadcast(broadcast);
+
+ // for USB we only have the one, so don't have to deal with additional
+ if (mAvailableIfaces.size() > 0) {
+ // Check if the user wants to be bothered
+ boolean tellUser = (Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.TETHER_NOTIFY, 0) == 1);
+
+ if (tellUser) {
+ showTetherAvailableNotification();
+ }
+ } else if (mActiveIfaces.size() > 0) {
+ showTetheredNotification();
+ } else {
+ clearNotification();
+ }
+ }
+
+ private void showTetherAvailableNotification() {
+ NotificationManager notificationManager = (NotificationManager)mContext.
+ getSystemService(Context.NOTIFICATION_SERVICE);
+ if (notificationManager == null) {
+ return;
+ }
+
+ Intent intent = new Intent();
+ intent.setClass(mContext, com.android.internal.app.TetherActivity.class);
+
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0);
+
+ Resources r = Resources.getSystem();
+ CharSequence title = r.getText(com.android.internal.R.string.
+ tether_available_notification_title);
+ CharSequence message = r.getText(com.android.internal.R.string.
+ tether_available_notification_message);
+
+ if(mTetheringNotification == null) {
+ mTetheringNotification = new Notification();
+ mTetheringNotification.when = 0;
+ }
+ mTetheringNotification.icon = com.android.internal.R.drawable.stat_sys_tether_usb;
+
+ boolean playSounds = false;
+ //playSounds = SystemProperties.get("persist.service.mount.playsnd", "1").equals("1");
+ if (playSounds) {
+ mTetheringNotification.defaults |= Notification.DEFAULT_SOUND;
+ } else {
+ mTetheringNotification.defaults &= ~Notification.DEFAULT_SOUND;
+ }
+
+ mTetheringNotification.flags = Notification.FLAG_ONGOING_EVENT;
+ mTetheringNotification.tickerText = title;
+ mTetheringNotification.setLatestEventInfo(mContext, title, message, pi);
+
+ notificationManager.notify(mTetheringNotification.icon, mTetheringNotification);
+
+ }
+
+ private void showTetheredNotification() {
+ NotificationManager notificationManager = (NotificationManager)mContext.
+ getSystemService(Context.NOTIFICATION_SERVICE);
+ if (notificationManager == null) {
+ return;
+ }
+
+ Intent intent = new Intent();
+ intent.setClass(mContext, com.android.internal.app.TetherActivity.class);
+
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0);
+
+ Resources r = Resources.getSystem();
+ CharSequence title = r.getText(com.android.internal.R.string.
+ tether_stop_notification_title);
+ CharSequence message = r.getText(com.android.internal.R.string.
+ tether_stop_notification_message);
+
+ if(mTetheringNotification == null) {
+ mTetheringNotification = new Notification();
+ mTetheringNotification.when = 0;
+ }
+ mTetheringNotification.icon = com.android.internal.R.drawable.stat_sys_tether_usb;
+
+ boolean playSounds = false;
+ //playSounds = SystemProperties.get("persist.service.mount.playsnd", "1").equals("1");
+ if (playSounds) {
+ mTetheringNotification.defaults |= Notification.DEFAULT_SOUND;
+ } else {
+ mTetheringNotification.defaults &= ~Notification.DEFAULT_SOUND;
+ }
+
+ mTetheringNotification.flags = Notification.FLAG_ONGOING_EVENT;
+ mTetheringNotification.tickerText = title;
+ mTetheringNotification.setLatestEventInfo(mContext, title, message, pi);
+
+ notificationManager.notify(mTetheringNotification.icon, mTetheringNotification);
+ }
+
+ private void clearNotification() {
+ NotificationManager notificationManager = (NotificationManager)mContext.
+ getSystemService(Context.NOTIFICATION_SERVICE);
+ if (notificationManager != null && mTetheringNotification != null) {
+ notificationManager.cancel(mTetheringNotification.icon);
+ mTetheringNotification = null;
+ }
+ }
+
+
+
+
+// TODO - remove this hack after we get proper USB detection
+ private class UMSStateReceiver extends BroadcastReceiver {
+ public void onReceive(Context content, Intent intent) {
+ if (intent.getAction().equals(Intent.ACTION_UMS_CONNECTED)) {
+ Tethering.this.handleTtyConnect();
+ } else if (intent.getAction().equals(Intent.ACTION_UMS_DISCONNECTED)) {
+ Tethering.this.handleTtyDisconnect();
+ }
+ }
+ }
+
+ private synchronized void handleTtyConnect() {
+ Log.d(TAG, "handleTtyConnect");
+ // for each of the available Tty not already supported by a ppp session,
+ // create a ppp session
+ // TODO - this should be data-driven rather than hard coded.
+ String[] allowedTtys = new String[1];
+ allowedTtys[0] = new String("ttyGS0");
+
+ IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
+ INetworkManagementService service = INetworkManagementService.Stub.asInterface(b);
+
+ String[] availableTtys;
+ try {
+ availableTtys = service.listTtys();
+ } catch (RemoteException e) {
+ Log.e(TAG, "error listing Ttys :" + e);
+ return;
+ }
+
+ for (String tty : availableTtys) {
+ for (String pattern : allowedTtys) {
+ if (tty.matches(pattern)) {
+ synchronized (this) {
+ if (!mActiveTtys.contains(tty)) {
+ // TODO - don't hardcode this
+ try {
+ // local, remote, dns
+ service.attachPppd(tty, "169.254.1.128", "169.254.1.1",
+ "169.254.1.128", "0.0.0.0");
+ } catch (Exception e) {
+ Log.e(TAG, "error calling attachPppd: " + e);
+ return;
+ }
+ Log.d(TAG, "started Pppd on tty " + tty);
+ mActiveTtys.add(tty);
+ // TODO - remove this after we detect the new iface
+ interfaceAdded("ppp0");
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private synchronized void handleTtyDisconnect() {
+ Log.d(TAG, "handleTtyDisconnect");
+
+ // TODO - this should be data-driven rather than hard coded.
+ String[] allowedTtys = new String[1];
+ allowedTtys[0] = new String("ttyGS0");
+
+ IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
+ INetworkManagementService service = INetworkManagementService.Stub.asInterface(b);
+
+ String[] availableTtys;
+ try {
+ availableTtys = service.listTtys();
+ } catch (RemoteException e) {
+ Log.e(TAG, "error listing Ttys :" + e);
+ return;
+ }
+
+ for (String tty : availableTtys) {
+ for (String pattern : allowedTtys) {
+ if (tty.matches(pattern)) {
+ synchronized (this) {
+ if (mActiveTtys.contains(tty)) {
+ try {
+ service.detachPppd(tty);
+ } catch (Exception e) {
+ Log.e(TAG, "error calling detachPppd on " + tty + " :" + e);
+ }
+ mActiveTtys.remove(tty);
+ // TODO - remove this after we detect the new iface
+ interfaceRemoved("ppp0");
+ return;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ public synchronized String[] getTetheredIfaces() {
+ int size = mActiveIfaces.size();
+ String[] result = new String[size];
+ size -= 1;
+ for (int i=0; i< size; i++) {
+ result[i] = mActiveIfaces.get(i);
+ }
+ return result;
+ }
+
+ public synchronized String[] getTetherableIfaces() {
+ int size = mAvailableIfaces.size();
+ String[] result = new String[size];
+ size -= 1;
+ for (int i=0; i< size; i++) {
+ result[i] = mActiveIfaces.get(i);
+ }
+ return result;
+ }
+}
diff --git a/services/java/com/android/server/status/NotificationData.java b/services/java/com/android/server/status/NotificationData.java
index 0a3411a1..784b781 100644
--- a/services/java/com/android/server/status/NotificationData.java
+++ b/services/java/com/android/server/status/NotificationData.java
@@ -19,7 +19,7 @@
public PendingIntent deleteIntent;
public String toString() {
- return "NotificationData(package=" + pkg + " tickerText=" + tickerText
+ return "NotificationData(package=" + pkg + " id=" + id + " tickerText=" + tickerText
+ " ongoingEvent=" + ongoingEvent + " contentIntent=" + contentIntent
+ " deleteIntent=" + deleteIntent
+ " clearable=" + clearable
diff --git a/services/java/com/android/server/status/NotificationViewList.java b/services/java/com/android/server/status/NotificationViewList.java
index 6229292..8f1633f 100644
--- a/services/java/com/android/server/status/NotificationViewList.java
+++ b/services/java/com/android/server/status/NotificationViewList.java
@@ -104,10 +104,25 @@
return null;
}
- // gets the index of the notification in its expanded parent view
+ // gets the index of the notification's view in its expanded parent view
int getExpandedIndex(StatusBarNotification notification) {
ArrayList<StatusBarNotification> list = notification.data.ongoingEvent ? mOngoing : mLatest;
- return list.size() - indexForKey(list, notification.key) - 1;
+ final IBinder key = notification.key;
+ int index = 0;
+ // (the view order is backwards from this list order)
+ for (int i=list.size()-1; i>=0; i--) {
+ StatusBarNotification item = list.get(i);
+ if (item.key == key) {
+ return index;
+ }
+ if (item.view != null) {
+ index++;
+ }
+ }
+ Log.e(StatusBarService.TAG, "Couldn't find notification in NotificationViewList.");
+ Log.e(StatusBarService.TAG, "notification=" + notification);
+ dump(notification);
+ return 0;
}
void clearViews() {
@@ -156,6 +171,13 @@
list.add(index, notification);
if (StatusBarService.SPEW) {
+ Log.d(StatusBarService.TAG, "NotificationViewList index=" + index);
+ dump(notification);
+ }
+ }
+
+ void dump(StatusBarNotification notification) {
+ if (StatusBarService.SPEW) {
String s = "";
for (int i=0; i<mOngoing.size(); i++) {
StatusBarNotification that = mOngoing.get(i);
@@ -168,7 +190,7 @@
}
s += " ";
}
- Log.d(StatusBarService.TAG, "NotificationViewList ongoing index=" + index + ": " + s);
+ Log.d(StatusBarService.TAG, "NotificationViewList ongoing: " + s);
s = "";
for (int i=0; i<mLatest.size(); i++) {
@@ -182,7 +204,7 @@
}
s += " ";
}
- Log.d(StatusBarService.TAG, "NotificationViewList latest index=" + index + ": " + s);
+ Log.d(StatusBarService.TAG, "NotificationViewList latest: " + s);
}
}
diff --git a/services/java/com/android/server/status/StatusBarService.java b/services/java/com/android/server/status/StatusBarService.java
index 7b722a5..55041fb 100644
--- a/services/java/com/android/server/status/StatusBarService.java
+++ b/services/java/com/android/server/status/StatusBarService.java
@@ -687,7 +687,8 @@
&& (oldData == null
|| oldData.tickerText == null
|| !CharSequences.equals(oldData.tickerText, n.tickerText))) {
- if ((mDisabled & StatusBarManager.DISABLE_NOTIFICATION_ICONS) == 0) {
+ if (0 == (mDisabled &
+ (StatusBarManager.DISABLE_NOTIFICATION_ICONS | StatusBarManager.DISABLE_NOTIFICATION_TICKER))) {
mTicker.addEntry(n, StatusBarIcon.getIcon(mContext, data), n.tickerText);
}
}
@@ -886,7 +887,8 @@
&& n.contentView.getPackage() != null
&& oldData.contentView.getPackage() != null
&& oldData.contentView.getPackage().equals(n.contentView.getPackage())
- && oldData.contentView.getLayoutId() == n.contentView.getLayoutId()) {
+ && oldData.contentView.getLayoutId() == n.contentView.getLayoutId()
+ && notification.view != null) {
mNotificationData.update(notification);
try {
n.contentView.reapply(mContext, notification.contentView);
@@ -1697,6 +1699,10 @@
setNotificationIconVisibility(true, com.android.internal.R.anim.fade_in);
}
}
+ } else if ((diff & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
+ if (mTicking && (net & StatusBarManager.DISABLE_NOTIFICATION_TICKER) != 0) {
+ mTicker.halt();
+ }
}
}
diff --git a/tests/AndroidTests/AndroidManifest.xml b/tests/AndroidTests/AndroidManifest.xml
index c2548b9..70d0e78 100644
--- a/tests/AndroidTests/AndroidManifest.xml
+++ b/tests/AndroidTests/AndroidManifest.xml
@@ -26,6 +26,8 @@
<uses-permission android:name="android.permission.GET_PACKAGE_SIZE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
+ <uses-permission android:name="android.permission.INSTALL_PACKAGES" />
+ <uses-permission android:name="android.permission.DELETE_PACKAGES" />
<uses-permission android:name="com.android.unit_tests.permission.TEST_GRANTED" />
<uses-permission android:name="com.google.android.googleapps.permission.ACCESS_GOOGLE_PASSWORD" />
<uses-permission android:name="com.google.android.googleapps.permission.GOOGLE_AUTH" />
diff --git a/tests/AndroidTests/res/raw/install b/tests/AndroidTests/res/raw/install
new file mode 100644
index 0000000..2ee1f3c
--- /dev/null
+++ b/tests/AndroidTests/res/raw/install
Binary files differ
diff --git a/tests/AndroidTests/src/com/android/unit_tests/PackageManagerTests.java b/tests/AndroidTests/src/com/android/unit_tests/PackageManagerTests.java
new file mode 100755
index 0000000..163ddd5
--- /dev/null
+++ b/tests/AndroidTests/src/com/android/unit_tests/PackageManagerTests.java
@@ -0,0 +1,387 @@
+/*
+ * Copyright (C) 2006 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.unit_tests;
+
+import android.net.Uri;
+import android.os.FileUtils;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+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.IPackageInstallObserver;
+import android.content.pm.IPackageStatsObserver;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageParser;
+import android.content.pm.PackageStats;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.res.Resources;
+import android.content.res.Resources.NotFoundException;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.LargeTest;
+import android.test.suitebuilder.annotation.MediumTest;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.test.suitebuilder.annotation.Suppress;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.os.Environment;
+import android.os.Handler;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.StatFs;
+
+public class PackageManagerTests extends AndroidTestCase {
+ private static final boolean localLOGV = false;
+ public static final String TAG="PackageManagerTests";
+ public final long MAX_WAIT_TIME=60*1000;
+ public final long WAIT_TIME_INCR=10*1000;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ }
+
+ private class PackageInstallObserver extends IPackageInstallObserver.Stub {
+ public int returnCode;
+ private boolean doneFlag = false;
+
+ public void packageInstalled(String packageName, int returnCode) {
+ synchronized(this) {
+ this.returnCode = returnCode;
+ doneFlag = true;
+ notifyAll();
+ }
+ }
+
+ public boolean isDone() {
+ return doneFlag;
+ }
+ }
+
+ abstract class GenericReceiver extends BroadcastReceiver {
+ private boolean doneFlag = false;
+ boolean received = false;
+ Intent intent;
+ IntentFilter filter;
+ abstract boolean notifyNow(Intent intent);
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (notifyNow(intent)) {
+ synchronized (this) {
+ received = true;
+ doneFlag = true;
+ this.intent = intent;
+ notifyAll();
+ }
+ }
+ }
+
+ public boolean isDone() {
+ return doneFlag;
+ }
+
+ public void setFilter(IntentFilter filter) {
+ this.filter = filter;
+ }
+ }
+
+ class InstallReceiver extends GenericReceiver {
+ String pkgName;
+
+ InstallReceiver(String pkgName) {
+ this.pkgName = pkgName;
+ IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
+ filter.addDataScheme("package");
+ super.setFilter(filter);
+ }
+
+ public boolean notifyNow(Intent intent) {
+ String action = intent.getAction();
+ if (!Intent.ACTION_PACKAGE_ADDED.equals(action)) {
+ return false;
+ }
+ Uri data = intent.getData();
+ String installedPkg = data.getEncodedSchemeSpecificPart();
+ if (pkgName.equals(installedPkg)) {
+ return true;
+ }
+ return false;
+ }
+ }
+
+ PackageManager getPm() {
+ return mContext.getPackageManager();
+ }
+
+ public boolean invokeInstallPackage(Uri packageURI, int flags,
+ final String pkgName, GenericReceiver receiver) throws Exception {
+ PackageInstallObserver observer = new PackageInstallObserver();
+ final boolean received = false;
+ mContext.registerReceiver(receiver, receiver.filter);
+ try {
+ // Wait on observer
+ synchronized(observer) {
+ getPm().installPackage(packageURI, observer, flags, null);
+ long waitTime = 0;
+ while((!observer.isDone()) && (waitTime < MAX_WAIT_TIME) ) {
+ observer.wait(WAIT_TIME_INCR);
+ waitTime += WAIT_TIME_INCR;
+ }
+ if(!observer.isDone()) {
+ throw new Exception("Timed out waiting for packageInstalled callback");
+ }
+ if (observer.returnCode != PackageManager.INSTALL_SUCCEEDED) {
+ return false;
+ }
+ synchronized (receiver) {
+ // Verify we received the broadcast
+ waitTime = 0;
+ while((!receiver.isDone()) && (waitTime < MAX_WAIT_TIME) ) {
+ receiver.wait(WAIT_TIME_INCR);
+ waitTime += WAIT_TIME_INCR;
+ }
+ if(!receiver.isDone()) {
+ throw new Exception("Timed out waiting for PACKAGE_ADDED notification");
+ }
+ return receiver.received;
+ }
+ }
+ } finally {
+ mContext.unregisterReceiver(receiver);
+ }
+ }
+
+ Uri getInstallablePackage(int fileResId, File outFile) {
+ Resources res = mContext.getResources();
+ InputStream is = null;
+ try {
+ is = res.openRawResource(fileResId);
+ } catch (NotFoundException e) {
+ failStr("Failed to load resource with id: " + fileResId);
+ }
+ FileUtils.setPermissions(outFile.getPath(),
+ FileUtils.S_IRWXU | FileUtils.S_IRWXG | FileUtils.S_IRWXO,
+ -1, -1);
+ assertTrue(FileUtils.copyToFile(is, outFile));
+ FileUtils.setPermissions(outFile.getPath(),
+ FileUtils.S_IRWXU | FileUtils.S_IRWXG | FileUtils.S_IRWXO,
+ -1, -1);
+ return Uri.fromFile(outFile);
+ }
+
+ private PackageParser.Package parsePackage(Uri packageURI) {
+ final String archiveFilePath = packageURI.getPath();
+ PackageParser packageParser = new PackageParser(archiveFilePath);
+ File sourceFile = new File(archiveFilePath);
+ DisplayMetrics metrics = new DisplayMetrics();
+ metrics.setToDefaults();
+ return packageParser.parsePackage(sourceFile, archiveFilePath, metrics, 0);
+ }
+
+ private void assertInstall(String pkgName, int flags) {
+ try {
+ ApplicationInfo info = getPm().getApplicationInfo(pkgName, 0);
+ assertNotNull(info);
+ assertEquals(pkgName, info.packageName);
+ File dataDir = Environment.getDataDirectory();
+ String appInstallPath = new File(dataDir, "app").getPath();
+ String drmInstallPath = new File(dataDir, "app-private").getPath();
+ File srcDir = new File(info.sourceDir);
+ String srcPath = srcDir.getParent();
+ File publicSrcDir = new File(info.publicSourceDir);
+ String publicSrcPath = publicSrcDir.getParent();
+
+ if ((flags & PackageManager.INSTALL_FORWARD_LOCK) != 0) {
+ assertTrue((info.flags & ApplicationInfo.FLAG_FORWARD_LOCK) != 0);
+ assertEquals(srcPath, drmInstallPath);
+ assertEquals(publicSrcPath, appInstallPath);
+ } else {
+ assertFalse((info.flags & ApplicationInfo.FLAG_FORWARD_LOCK) != 0);
+ if ((flags & PackageManager.INSTALL_ON_SDCARD) != 0) {
+ assertTrue((info.flags & ApplicationInfo.FLAG_ON_SDCARD) != 0);
+ // Hardcoded for now
+ assertTrue(srcPath.startsWith("/asec"));
+ assertTrue(publicSrcPath.startsWith("/asec"));
+ } else {
+ assertEquals(srcPath, appInstallPath);
+ assertEquals(publicSrcPath, appInstallPath);
+ assertFalse((info.flags & ApplicationInfo.FLAG_ON_SDCARD) != 0);
+ }
+ }
+ } catch (NameNotFoundException e) {
+ failStr("failed with exception : " + e);
+ }
+ }
+
+ class InstallParams {
+ String outFileName;
+ Uri packageURI;
+ PackageParser.Package pkg;
+ InstallParams(PackageParser.Package pkg, String outFileName, Uri packageURI) {
+ this.outFileName = outFileName;
+ this.packageURI = packageURI;
+ this.pkg = pkg;
+ }
+ }
+
+ /*
+ * Utility function that reads a apk bundled as a raw resource
+ * copies it into own data directory and invokes
+ * PackageManager api to install it.
+ */
+ public InstallParams installFromRawResource(int flags, boolean cleanUp) {
+ String outFileName = "install.apk";
+ File filesDir = mContext.getFilesDir();
+ File outFile = new File(filesDir, outFileName);
+ Uri packageURI = getInstallablePackage(R.raw.install, outFile);
+ PackageParser.Package pkg = parsePackage(packageURI);
+ assertNotNull(pkg);
+ InstallReceiver receiver = new InstallReceiver(pkg.packageName);
+ try {
+ try {
+ assertTrue(invokeInstallPackage(packageURI, flags,
+ pkg.packageName, receiver));
+ } catch (Exception e) {
+ failStr("Failed with exception : " + e);
+ }
+ // Verify installed information
+ assertInstall(pkg.packageName, flags);
+ return new InstallParams(pkg, outFileName, packageURI);
+ } finally {
+ if (cleanUp) {
+ getPm().deletePackage(pkg.packageName, null, 0);
+ if (outFile != null && outFile.exists()) {
+ outFile.delete();
+ }
+ }
+ }
+ }
+
+ @MediumTest
+ public void testInstallNormalInternal() {
+ installFromRawResource(0, true);
+ }
+
+ @MediumTest
+ public void testInstallFwdLockedInternal() {
+ installFromRawResource(PackageManager.INSTALL_FORWARD_LOCK, true);
+ }
+
+ @MediumTest
+ public void testInstallSdcard() {
+ installFromRawResource(PackageManager.INSTALL_ON_SDCARD, true);
+ }
+
+ /* ------------------------- Test replacing packages --------------*/
+ class ReplaceReceiver extends GenericReceiver {
+ String pkgName;
+ boolean removed = false;
+
+ ReplaceReceiver(String pkgName) {
+ this.pkgName = pkgName;
+ filter = new IntentFilter(Intent.ACTION_PACKAGE_REMOVED);
+ filter.addAction(Intent.ACTION_PACKAGE_REPLACED);
+ super.setFilter(filter);
+ }
+
+ public boolean notifyNow(Intent intent) {
+ String action = intent.getAction();
+ if (Intent.ACTION_PACKAGE_REMOVED.equals(action) ||
+ Intent.ACTION_PACKAGE_REPLACED.equals(action)) {
+ Uri data = intent.getData();
+ String installedPkg = data.getEncodedSchemeSpecificPart();
+ if (pkgName.equals(installedPkg)) {
+ if (removed) {
+ return true;
+ } else {
+ removed = true;
+ }
+ }
+ }
+ return false;
+ }
+ }
+
+ /*
+ * Utility function that reads a apk bundled as a raw resource
+ * copies it into own data directory and invokes
+ * PackageManager api to install first and then replace it
+ * again.
+ */
+ public void replaceFromRawResource(int flags) {
+ InstallParams ip = installFromRawResource(flags, false);
+ boolean result = ((flags & PackageManager.INSTALL_REPLACE_EXISTING) != 0);
+ GenericReceiver receiver;
+ if (result) {
+ receiver = new ReplaceReceiver(ip.pkg.packageName);
+ } else {
+ receiver = new InstallReceiver(ip.pkg.packageName);
+ }
+ try {
+ try {
+ assertEquals(invokeInstallPackage(ip.packageURI, flags,
+ ip.pkg.packageName, receiver), result);
+ if (result) {
+ assertInstall(ip.pkg.packageName, flags);
+ }
+ } catch (Exception e) {
+ failStr("Failed with exception : " + e);
+ }
+ } finally {
+ getPm().deletePackage(ip.pkg.packageName, null, 0);
+ File outFile = new File(ip.outFileName);
+ if (outFile != null && outFile.exists()) {
+ outFile.delete();
+ }
+ }
+ }
+
+ @MediumTest
+ public void testReplaceFailNormalInternal() {
+ replaceFromRawResource(0);
+ }
+
+ @MediumTest
+ public void testReplaceFailFwdLockedInternal() {
+ replaceFromRawResource(PackageManager.INSTALL_FORWARD_LOCK);
+ }
+
+ @MediumTest
+ public void testReplaceFailSdcard() {
+ replaceFromRawResource(PackageManager.INSTALL_ON_SDCARD);
+ }
+
+ void failStr(String errMsg) {
+ Log.w(TAG, "errMsg="+errMsg);
+ fail(errMsg);
+ }
+ void failStr(Exception e) {
+ Log.w(TAG, "e.getMessage="+e.getMessage());
+ Log.w(TAG, "e="+e);
+ }
+}
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java b/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java
index e8a66c1..2667520 100644
--- a/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java
+++ b/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java
@@ -670,7 +670,12 @@
public boolean onCreateWindow(WebView view, boolean dialog,
boolean userGesture, Message resultMsg) {
if (!mCanOpenWindows) {
- return false;
+ // We can't open windows, so just send null back.
+ WebView.WebViewTransport transport =
+ (WebView.WebViewTransport) resultMsg.obj;
+ transport.setWebView(null);
+ resultMsg.sendToTarget();
+ return true;
}
// We never display the new window, just create the view and
@@ -688,6 +693,11 @@
resultMsg.sendToTarget();
return true;
}
+
+ @Override
+ public void onCloseWindow(WebView view) {
+ view.destroy();
+ }
};
private static class NewWindowWebView extends WebView {
diff --git a/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java b/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
index ffc2cbc..f548145 100644
--- a/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
+++ b/tests/StatusBar/src/com/android/statusbartest/NotificationTestList.java
@@ -121,6 +121,32 @@
}
},
+ new Test("Bad resource #2") {
+ public void run()
+ {
+ Notification n = new Notification(NotificationTestList.this,
+ R.drawable.ic_statusbar_missedcall,
+ null, System.currentTimeMillis()-(1000*60*60*24),
+ "(453) 123-2328",
+ "", null);
+ n.contentView.setInt(1 /*bogus*/, "bogus method", 666);
+ mNM.notify(2, n);
+ }
+ },
+
+ new Test("Bad resource #3") {
+ public void run()
+ {
+ Notification n = new Notification(NotificationTestList.this,
+ R.drawable.ic_statusbar_missedcall,
+ null, System.currentTimeMillis()-(1000*60*60*24),
+ "(453) 123-2328",
+ "", null);
+ n.contentView.setInt(1 /*bogus*/, "bogus method", 666);
+ mNM.notify(3, n);
+ }
+ },
+
new Test("Times") {
public void run()
{
@@ -405,6 +431,16 @@
}
},
+ new Test("Persistent #3") {
+ public void run() {
+ Notification n = new Notification(R.drawable.icon2, "tock tock tock",
+ System.currentTimeMillis());
+ n.setLatestEventInfo(NotificationTestList.this, "Persistent #3",
+ "Notify me!!!", makeIntent());
+ mNM.notify(3, n);
+ }
+ },
+
new Test("Persistent #2 Vibrate") {
public void run() {
Notification n = new Notification(R.drawable.icon2, "tock tock tock",
diff --git a/tests/StatusBar/src/com/android/statusbartest/StatusBarTest.java b/tests/StatusBar/src/com/android/statusbartest/StatusBarTest.java
index 275e5cb6..06506fb 100644
--- a/tests/StatusBar/src/com/android/statusbartest/StatusBarTest.java
+++ b/tests/StatusBar/src/com/android/statusbartest/StatusBarTest.java
@@ -62,6 +62,11 @@
mStatusBarManager.disable(StatusBarManager.DISABLE_NOTIFICATION_ALERTS);
}
},
+ new Test("Disable Ticker") {
+ public void run() {
+ mStatusBarManager.disable(StatusBarManager.DISABLE_NOTIFICATION_TICKER);
+ }
+ },
new Test("Disable Expand in 3 sec.") {
public void run() {
mHandler.postDelayed(new Runnable() {
@@ -80,7 +85,7 @@
}, 3000);
}
},
- new Test("Disable Both in 3 sec.") {
+ new Test("Disable Expand + Notifications in 3 sec.") {
public void run() {
mHandler.postDelayed(new Runnable() {
public void run() {
@@ -90,7 +95,12 @@
}, 3000);
}
},
- new Test("Disable None in 3 sec.") {
+ new Test("Enable everything") {
+ public void run() {
+ mStatusBarManager.disable(0);
+ }
+ },
+ new Test("Enable everything in 3 sec.") {
public void run() {
mHandler.postDelayed(new Runnable() {
public void run() {