diff options
author | Mathias Agopian <mathias@google.com> | 2009-06-19 17:41:14 -0700 |
---|---|---|
committer | Mathias Agopian <mathias@google.com> | 2009-06-19 17:41:14 -0700 |
commit | f73bbd042367b65780316d2335784686dedd0459 (patch) | |
tree | 913a9ea96c37d617789914ec83f81ed0611d4d68 | |
parent | 6edf5af578c1ab1fcd44b7c08ca371456e4b7430 (diff) | |
parent | aba6af9277b34c739b83d3d2d908724c3f7ec1b4 (diff) | |
download | frameworks_base-f73bbd042367b65780316d2335784686dedd0459.zip frameworks_base-f73bbd042367b65780316d2335784686dedd0459.tar.gz frameworks_base-f73bbd042367b65780316d2335784686dedd0459.tar.bz2 |
Merge commit 'goog/master' into merge_master
255 files changed, 7798 insertions, 2412 deletions
@@ -73,10 +73,8 @@ LOCAL_SRC_FILES += \ core/java/android/app/IActivityPendingResult.aidl \ core/java/android/app/IActivityWatcher.aidl \ core/java/android/app/IAlarmManager.aidl \ - core/java/android/app/IBackupAgent.aidl \ + core/java/android/app/IBackupAgent.aidl \ core/java/android/app/IInstrumentationWatcher.aidl \ - core/java/android/app/IIntentReceiver.aidl \ - core/java/android/app/IIntentSender.aidl \ core/java/android/app/INotificationManager.aidl \ core/java/android/app/ISearchManager.aidl \ core/java/android/app/ISearchManagerCallback.aidl \ @@ -92,6 +90,8 @@ LOCAL_SRC_FILES += \ core/java/android/bluetooth/IBluetoothDevice.aidl \ core/java/android/bluetooth/IBluetoothHeadset.aidl \ core/java/android/content/IContentService.aidl \ + core/java/android/content/IIntentReceiver.aidl \ + core/java/android/content/IIntentSender.aidl \ core/java/android/content/ISyncAdapter.aidl \ core/java/android/content/ISyncContext.aidl \ core/java/android/content/ISyncStatusObserver.aidl \ @@ -203,6 +203,7 @@ aidl_files := \ frameworks/base/core/java/android/app/PendingIntent.aidl \ frameworks/base/core/java/android/content/ComponentName.aidl \ frameworks/base/core/java/android/content/Intent.aidl \ + frameworks/base/core/java/android/content/IntentSender.aidl \ frameworks/base/core/java/android/content/SyncStats.aidl \ frameworks/base/core/java/android/content/res/Configuration.aidl \ frameworks/base/core/java/android/appwidget/AppWidgetProviderInfo.aidl \ diff --git a/api/current.xml b/api/current.xml index c436b2d..3174f09 100644 --- a/api/current.xml +++ b/api/current.xml @@ -25161,6 +25161,17 @@ <parameter name="flags" type="int"> </parameter> </method> +<method name="getIntentSender" + return="android.content.IntentSender" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> <method name="getService" return="android.app.PendingIntent" abstract="false" @@ -30524,6 +30535,17 @@ visibility="public" > </method> +<method name="getApplicationInfo" + return="android.content.pm.ApplicationInfo" + abstract="true" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> <method name="getAssets" return="android.content.res.AssetManager" abstract="true" @@ -31887,6 +31909,17 @@ visibility="public" > </method> +<method name="getApplicationInfo" + return="android.content.pm.ApplicationInfo" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> <method name="getAssets" return="android.content.res.AssetManager" abstract="false" @@ -32882,6 +32915,70 @@ </exception> </method> </interface> +<interface name="IIntentReceiver" + abstract="true" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<implements name="android.os.IInterface"> +</implements> +<method name="performReceive" + return="void" + abstract="true" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="intent" type="android.content.Intent"> +</parameter> +<parameter name="resultCode" type="int"> +</parameter> +<parameter name="data" type="java.lang.String"> +</parameter> +<parameter name="extras" type="android.os.Bundle"> +</parameter> +<parameter name="ordered" type="boolean"> +</parameter> +<exception name="RemoteException" type="android.os.RemoteException"> +</exception> +</method> +</interface> +<interface name="IIntentSender" + abstract="true" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<implements name="android.os.IInterface"> +</implements> +<method name="send" + return="int" + abstract="true" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="code" type="int"> +</parameter> +<parameter name="intent" type="android.content.Intent"> +</parameter> +<parameter name="resolvedType" type="java.lang.String"> +</parameter> +<parameter name="finishedReceiver" type="android.content.IIntentReceiver"> +</parameter> +<exception name="RemoteException" type="android.os.RemoteException"> +</exception> +</method> +</interface> <class name="Intent" extends="java.lang.Object" abstract="false" @@ -33364,7 +33461,7 @@ synchronized="false" static="true" final="false" - deprecated="not deprecated" + deprecated="deprecated" visibility="public" > <parameter name="uri" type="java.lang.String"> @@ -33415,6 +33512,17 @@ <parameter name="defaultValue" type="long"> </parameter> </method> +<method name="getPackage" + return="java.lang.String" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> <method name="getParcelableArrayExtra" return="android.os.Parcelable[]" abstract="false" @@ -33614,6 +33722,23 @@ <exception name="XmlPullParserException" type="org.xmlpull.v1.XmlPullParserException"> </exception> </method> +<method name="parseUri" + return="android.content.Intent" + abstract="false" + native="false" + synchronized="false" + static="true" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="uri" type="java.lang.String"> +</parameter> +<parameter name="flags" type="int"> +</parameter> +<exception name="URISyntaxException" type="java.net.URISyntaxException"> +</exception> +</method> <method name="putExtra" return="android.content.Intent" abstract="false" @@ -34287,6 +34412,19 @@ <parameter name="flags" type="int"> </parameter> </method> +<method name="setPackage" + return="android.content.Intent" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="packageName" type="java.lang.String"> +</parameter> +</method> <method name="setType" return="android.content.Intent" abstract="false" @@ -34307,9 +34445,22 @@ synchronized="false" static="false" final="false" + deprecated="deprecated" + visibility="public" +> +</method> +<method name="toUri" + return="java.lang.String" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" deprecated="not deprecated" visibility="public" > +<parameter name="flags" type="int"> +</parameter> </method> <method name="writeToParcel" return="void" @@ -34953,6 +35104,17 @@ visibility="public" > </field> +<field name="ACTION_POWER_USAGE_SUMMARY" + type="java.lang.String" + transient="false" + volatile="false" + value=""android.intent.action.POWER_USAGE_SUMMARY"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="ACTION_PROVIDER_CHANGED" type="java.lang.String" transient="false" @@ -35063,6 +35225,17 @@ visibility="public" > </field> +<field name="ACTION_SEND_MULTIPLE" + type="java.lang.String" + transient="false" + volatile="false" + value=""android.intent.action.SEND_MULTIPLE"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="ACTION_SET_WALLPAPER" type="java.lang.String" transient="false" @@ -35700,6 +35873,17 @@ visibility="public" > </field> +<field name="FILL_IN_PACKAGE" + type="int" + transient="false" + volatile="false" + value="16" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="FLAG_ACTIVITY_BROUGHT_TO_FRONT" type="int" transient="false" @@ -35909,6 +36093,17 @@ visibility="public" > </field> +<field name="URI_INTENT_SCHEME" + type="int" + transient="false" + volatile="false" + value="1" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> </class> <class name="Intent.FilterComparison" extends="java.lang.Object" @@ -36923,6 +37118,190 @@ </parameter> </constructor> </class> +<class name="IntentSender" + extends="java.lang.Object" + abstract="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<implements name="android.os.Parcelable"> +</implements> +<constructor name="IntentSender" + type="android.content.IntentSender" + static="false" + final="false" + deprecated="not deprecated" + visibility="protected" +> +<parameter name="target" type="android.content.IIntentSender"> +</parameter> +</constructor> +<constructor name="IntentSender" + type="android.content.IntentSender" + static="false" + final="false" + deprecated="not deprecated" + visibility="protected" +> +<parameter name="target" type="android.os.IBinder"> +</parameter> +</constructor> +<method name="describeContents" + return="int" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> +<method name="readIntentSenderOrNullFromParcel" + return="android.content.IntentSender" + abstract="false" + native="false" + synchronized="false" + static="true" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="in" type="android.os.Parcel"> +</parameter> +</method> +<method name="sendIntent" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="context" type="android.content.Context"> +</parameter> +<parameter name="code" type="int"> +</parameter> +<parameter name="intent" type="android.content.Intent"> +</parameter> +<parameter name="onFinished" type="android.content.IntentSender.OnFinished"> +</parameter> +<parameter name="handler" type="android.os.Handler"> +</parameter> +<exception name="IntentSender.SendIntentException" type="android.content.IntentSender.SendIntentException"> +</exception> +</method> +<method name="writeIntentSenderOrNullToParcel" + return="void" + abstract="false" + native="false" + synchronized="false" + static="true" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="sender" type="android.content.IntentSender"> +</parameter> +<parameter name="out" type="android.os.Parcel"> +</parameter> +</method> +<method name="writeToParcel" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="out" type="android.os.Parcel"> +</parameter> +<parameter name="flags" type="int"> +</parameter> +</method> +<field name="CREATOR" + type="android.os.Parcelable.Creator" + transient="false" + volatile="false" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +</class> +<interface name="IntentSender.OnFinished" + abstract="true" + static="true" + final="false" + deprecated="not deprecated" + visibility="public" +> +<method name="onSendFinished" + return="void" + abstract="true" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="IntentSender" type="android.content.IntentSender"> +</parameter> +<parameter name="intent" type="android.content.Intent"> +</parameter> +<parameter name="resultCode" type="int"> +</parameter> +<parameter name="resultData" type="java.lang.String"> +</parameter> +<parameter name="resultExtras" type="android.os.Bundle"> +</parameter> +</method> +</interface> +<class name="IntentSender.SendIntentException" + extends="android.util.AndroidException" + abstract="false" + static="true" + final="false" + deprecated="not deprecated" + visibility="public" +> +<constructor name="IntentSender.SendIntentException" + type="android.content.IntentSender.SendIntentException" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</constructor> +<constructor name="IntentSender.SendIntentException" + type="android.content.IntentSender.SendIntentException" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="name" type="java.lang.String"> +</parameter> +</constructor> +<constructor name="IntentSender.SendIntentException" + type="android.content.IntentSender.SendIntentException" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +<parameter name="cause" type="java.lang.Exception"> +</parameter> +</constructor> +</class> <class name="MutableContextWrapper" extends="android.content.ContextWrapper" abstract="false" @@ -38215,6 +38594,17 @@ visibility="public" > </field> +<field name="FLAG_SUPPORTS_LARGE_SCREENS" + type="int" + transient="false" + volatile="false" + value="512" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="FLAG_SYSTEM" type="int" transient="false" @@ -38288,16 +38678,6 @@ visibility="public" > </field> -<field name="expandable" - type="boolean" - transient="false" - volatile="false" - static="false" - final="false" - deprecated="not deprecated" - visibility="public" -> -</field> <field name="flags" type="int" transient="false" @@ -40029,23 +40409,6 @@ <parameter name="flags" type="int"> </parameter> </method> -<method name="resolveActivity" - return="android.content.pm.ResolveInfo" - abstract="true" - native="false" - synchronized="false" - static="false" - final="false" - deprecated="not deprecated" - visibility="public" -> -<parameter name="intent" type="android.content.Intent"> -</parameter> -<parameter name="flags" type="int"> -</parameter> -<parameter name="packageName" type="java.lang.String"> -</parameter> -</method> <method name="resolveContentProvider" return="android.content.pm.ProviderInfo" abstract="true" @@ -40187,17 +40550,6 @@ visibility="public" > </field> -<field name="GET_EXPANDABLE" - type="int" - transient="false" - volatile="false" - value="131072" - static="true" - final="true" - deprecated="not deprecated" - visibility="public" -> -</field> <field name="GET_GIDS" type="int" transient="false" @@ -109012,6 +109364,17 @@ visibility="public" > </field> +<field name="TTS_DEFAULT_COUNTRY" + type="java.lang.String" + transient="false" + volatile="false" + value=""tts_default_country"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="TTS_DEFAULT_LANG" type="java.lang.String" transient="false" @@ -109056,6 +109419,17 @@ visibility="public" > </field> +<field name="TTS_DEFAULT_VARIANT" + type="java.lang.String" + transient="false" + volatile="false" + value=""tts_default_variant"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="TTS_USE_DEFAULTS" type="java.lang.String" transient="false" @@ -117589,6 +117963,17 @@ visibility="public" > </method> +<method name="getApplicationInfo" + return="android.content.pm.ApplicationInfo" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> <method name="getAssets" return="android.content.res.AssetManager" abstract="false" @@ -118976,23 +119361,6 @@ </parameter> <parameter name="flags" type="int"> </parameter> -<parameter name="packageName" type="java.lang.String"> -</parameter> -</method> -<method name="resolveActivity" - return="android.content.pm.ResolveInfo" - abstract="false" - native="false" - synchronized="false" - static="false" - final="false" - deprecated="not deprecated" - visibility="public" -> -<parameter name="intent" type="android.content.Intent"> -</parameter> -<parameter name="flags" type="int"> -</parameter> </method> <method name="resolveContentProvider" return="android.content.pm.ProviderInfo" diff --git a/cmds/backup/backup.cpp b/cmds/backup/backup.cpp index 22dd486..d4e669b 100644 --- a/cmds/backup/backup.cpp +++ b/cmds/backup/backup.cpp @@ -64,22 +64,14 @@ perform_list(const char* filename) } BackupDataReader reader(fd); + bool done; int type; - while (reader.ReadNextHeader(&type) == 0) { + while (reader.ReadNextHeader(&done, &type) == 0) { + if (done) { + break; + } switch (type) { - case BACKUP_HEADER_APP_V1: - { - String8 packageName; - int cookie; - err = reader.ReadAppHeader(&packageName, &cookie); - if (err == 0) { - printf("App header: %s 0x%08x (%d)\n", packageName.string(), cookie, cookie); - } else { - printf("Error reading app header\n"); - } - break; - } case BACKUP_HEADER_ENTITY_V1: { String8 key; @@ -92,17 +84,6 @@ perform_list(const char* filename) } break; } - case BACKUP_FOOTER_APP_V1: - { - int cookie; - err = reader.ReadAppFooter(&cookie); - if (err == 0) { - printf(" App footer: 0x%08x (%d)\n", cookie, cookie); - } else { - printf(" Error reading entity header\n"); - } - break; - } default: { printf("Unknown chunk type: 0x%08x\n", type); diff --git a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java index 467cac1..841e3df 100644 --- a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java +++ b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java @@ -36,9 +36,14 @@ public final class Bmgr { private String mCurArgData; public static void main(String[] args) { - new Bmgr().run(args); + try { + new Bmgr().run(args); + } catch (Exception e) { + System.err.println("Exception caught:"); + e.printStackTrace(); + } } - + public void run(String[] args) { boolean validCommand = false; if (args.length < 1) { @@ -70,6 +75,19 @@ public final class Bmgr { doList(); return; } + + if ("restore".equals(op)) { + doRestore(); + return; + } + + if ("transport".equals(op)) { + doTransport(); + return; + } + + System.err.println("Unknown command"); + showUsage(); } private void doRun() { @@ -103,6 +121,19 @@ public final class Bmgr { } } + private void doTransport() { + try { + int which = Integer.parseInt(nextArg()); + int old = mBmgr.selectBackupTransport(which); + System.out.println("Selected transport " + which + " (formerly " + old + ")"); + } catch (NumberFormatException e) { + showUsage(); + } catch (RemoteException e) { + System.err.println(e.toString()); + System.err.println(BMGR_NOT_RUNNING_ERR); + } + } + private void doList() { String arg = nextArg(); // sets, transports, packages set# if ("transports".equals(arg)) { @@ -114,6 +145,10 @@ public final class Bmgr { try { int curTransport = mBmgr.getCurrentTransport(); mRestore = mBmgr.beginRestoreSession(curTransport); + if (mRestore == null) { + System.err.println(BMGR_NOT_RUNNING_ERR); + return; + } if ("sets".equals(arg)) { doListRestoreSets(); @@ -127,13 +162,12 @@ public final class Bmgr { } private void doListTransports() { - } private void doListRestoreSets() { try { RestoreSet[] sets = mRestore.getAvailableRestoreSets(); - if (sets.length == 0) { + if (sets == null || sets.length == 0) { System.out.println("No restore sets available"); } else { for (RestoreSet s : sets) { @@ -146,6 +180,37 @@ public final class Bmgr { } } + private void doRestore() { + int token; + try { + token = Integer.parseInt(nextArg()); + } catch (NumberFormatException e) { + showUsage(); + return; + } + + try { + int curTransport = mBmgr.getCurrentTransport(); + mRestore = mBmgr.beginRestoreSession(curTransport); + if (mRestore == null) { + System.err.println(BMGR_NOT_RUNNING_ERR); + return; + } + RestoreSet[] sets = mRestore.getAvailableRestoreSets(); + for (RestoreSet s : sets) { + if (s.token == token) { + System.out.println("Scheduling restore: " + s.name); + mRestore.performRestore(token); + break; + } + } + mRestore.endRestoreSession(); + } catch (RemoteException e) { + System.err.println(e.toString()); + System.err.println(BMGR_NOT_RUNNING_ERR); + } + } + private String nextArg() { if (mNextArg >= mArgs.length) { return null; @@ -161,7 +226,7 @@ public final class Bmgr { System.err.println(" bmgr list sets"); System.err.println(" #bmgr list transports"); System.err.println(" #bmgr transport which#"); - System.err.println(" #bmgr restore set#"); + System.err.println(" bmgr restore token#"); System.err.println(" bmgr run"); } }
\ No newline at end of file diff --git a/cmds/keystore/commands.c b/cmds/keystore/commands.c index e53cece..17dd060 100644 --- a/cmds/keystore/commands.c +++ b/cmds/keystore/commands.c @@ -1,5 +1,5 @@ /* -** Copyright 2008, The Android Open Source Project +** Copyright 2009, The Android Open Source Project ** ** Licensed under the Apache License, Version 2.0 (the "License"); ** you may not use this file except in compliance with the License. @@ -30,7 +30,8 @@ static DIR *open_keystore(const char *dir) return d; } -static int list_files(const char *dir, char reply[REPLY_MAX]) { +static int list_files(const char *dir, char reply[REPLY_MAX]) +{ struct dirent *de; DIR *d; @@ -39,7 +40,9 @@ static int list_files(const char *dir, char reply[REPLY_MAX]) { } reply[0]=0; while ((de = readdir(d))) { - if (de->d_type != DT_REG) continue; + if (de->d_type != DT_DIR) continue; + if ((strcmp(DOT, de->d_name) == 0) || + (strcmp(DOTDOT, de->d_name) == 0)) continue; if (reply[0] != 0) strlcat(reply, " ", REPLY_MAX); if (strlcat(reply, de->d_name, REPLY_MAX) >= REPLY_MAX) { LOGE("reply is too long(too many files under '%s'\n", dir); @@ -50,31 +53,25 @@ static int list_files(const char *dir, char reply[REPLY_MAX]) { return 0; } -static int copy_keyfile(const char *keystore, const char *srcfile) { - int srcfd, dstfd; - int length; - char buf[2048]; - char dstfile[KEYNAME_LENGTH]; - const char *filename = strrchr(srcfile, '/'); +static int copy_keyfile(const char *src, int src_type, const char *dstfile) { + int srcfd = -1, dstfd; + char buf[REPLY_MAX]; - strlcpy(dstfile, keystore, KEYNAME_LENGTH); - strlcat(dstfile, "/", KEYNAME_LENGTH); - if (strlcat(dstfile, filename ? filename + 1 : srcfile, - KEYNAME_LENGTH) >= KEYNAME_LENGTH) { - LOGE("keyname is too long '%s'\n", srcfile); - return -1; - } - - if ((srcfd = open(srcfile, O_RDONLY)) == -1) { - LOGE("Cannot open the original file '%s'\n", srcfile); + if ((src_type == IS_FILE) && (srcfd = open(src, O_RDONLY)) == -1) { + LOGE("Cannot open the original file '%s'\n", src); return -1; } if ((dstfd = open(dstfile, O_CREAT|O_RDWR)) == -1) { LOGE("Cannot open the destination file '%s'\n", dstfile); return -1; } - while((length = read(srcfd, buf, 2048)) > 0) { - write(dstfd, buf, length); + if (src_type == IS_FILE) { + int length; + while((length = read(srcfd, buf, REPLY_MAX)) > 0) { + write(dstfd, buf, length); + } + } else { + write(dstfd, src, strlen(src)); } close(srcfd); close(dstfd); @@ -82,60 +79,149 @@ static int copy_keyfile(const char *keystore, const char *srcfile) { return 0; } -static int install_key(const char *dir, const char *keyfile) +static int install_key(const char *path, const char *certname, const char *src, + int src_is_file, char *dstfile) { struct dirent *de; + char fullpath[KEYNAME_LENGTH]; DIR *d; - if ((d = open_keystore(dir)) == NULL) { + if (snprintf(fullpath, sizeof(fullpath), "%s/%s/", path, certname) + >= KEYNAME_LENGTH) { + LOGE("cert name '%s' is too long.\n", certname); + return -1; + } + + if ((d = open_keystore(fullpath)) == NULL) { + LOGE("Can not open the keystore '%s'\n", fullpath); + return -1; + } + closedir(d); + if (strlcat(fullpath, dstfile, KEYNAME_LENGTH) >= KEYNAME_LENGTH) { + LOGE("cert name '%s' is too long.\n", certname); return -1; } - return copy_keyfile(dir, keyfile); + return copy_keyfile(src, src_is_file, fullpath); } -static int remove_key(const char *dir, const char *keyfile) +static int get_key(const char *path, const char *keyname, const char *file, + char reply[REPLY_MAX]) +{ + struct dirent *de; + char filename[KEYNAME_LENGTH]; + int fd; + + if (snprintf(filename, sizeof(filename), "%s/%s/%s", path, keyname, file) + >= KEYNAME_LENGTH) { + LOGE("cert name '%s' is too long.\n", keyname); + return -1; + } + + if ((fd = open(filename, O_RDONLY)) == -1) { + return -1; + } + close(fd); + strlcpy(reply, filename, REPLY_MAX); + return 0; +} + +static int remove_key(const char *dir, const char *key) { char dstfile[KEYNAME_LENGTH]; + char *keyfile[4] = { USER_KEY, USER_P12_CERT, USER_CERTIFICATE, + CA_CERTIFICATE }; + int i, count = 0; + + for ( i = 0 ; i < 4 ; i++) { + if (snprintf(dstfile, KEYNAME_LENGTH, "%s/%s/%s", dir, key, keyfile[i]) + >= KEYNAME_LENGTH) { + LOGE("keyname is too long '%s'\n", key); + return -1; + } + if (unlink(dstfile) == 0) count++; + } - strlcpy(dstfile, dir, KEYNAME_LENGTH); - strlcat(dstfile, "/", KEYNAME_LENGTH); - if (strlcat(dstfile, keyfile, KEYNAME_LENGTH) >= KEYNAME_LENGTH) { - LOGE("keyname is too long '%s'\n", keyfile); + if (count == 0) { + LOGE("can not clean up '%s' keys or not exist\n", key); return -1; } - if (unlink(dstfile)) { - LOGE("cannot delete '%s': %s\n", dstfile, strerror(errno)); + + snprintf(dstfile, KEYNAME_LENGTH, "%s/%s", dir, key); + if (rmdir(dstfile)) { + LOGE("can not clean up '%s' directory\n", key); return -1; } return 0; } -int list_certs(char reply[REPLY_MAX]) +int list_user_certs(char reply[REPLY_MAX]) { return list_files(CERTS_DIR, reply); } -int list_userkeys(char reply[REPLY_MAX]) +int list_ca_certs(char reply[REPLY_MAX]) { - return list_files(USERKEYS_DIR, reply); + return list_files(CACERTS_DIR, reply); } -int install_cert(const char *certfile) +int install_user_cert(const char *keyname, const char *cert, const char *key) { - return install_key(CERTS_DIR, certfile); + if (install_key(CERTS_DIR, keyname, cert, IS_FILE, USER_CERTIFICATE) == 0) { + return install_key(CERTS_DIR, keyname, key, IS_FILE, USER_KEY); + } + return -1; } -int install_userkey(const char *keyfile) +int install_ca_cert(const char *keyname, const char *certfile) { - return install_key(USERKEYS_DIR, keyfile); + return install_key(CACERTS_DIR, keyname, certfile, IS_FILE, CA_CERTIFICATE); +} + +int install_p12_cert(const char *keyname, const char *certfile) +{ + return install_key(CERTS_DIR, keyname, certfile, IS_FILE, USER_P12_CERT); +} + +int add_ca_cert(const char *keyname, const char *certificate) +{ + return install_key(CACERTS_DIR, keyname, certificate, IS_CONTENT, + CA_CERTIFICATE); +} + +int add_user_cert(const char *keyname, const char *certificate) +{ + return install_key(CERTS_DIR, keyname, certificate, IS_CONTENT, + USER_CERTIFICATE); +} + +int add_user_key(const char *keyname, const char *key) +{ + return install_key(CERTS_DIR, keyname, key, IS_CONTENT, USER_KEY); +} + +int get_ca_cert(const char *keyname, char reply[REPLY_MAX]) +{ + return get_key(CACERTS_DIR, keyname, CA_CERTIFICATE, reply); +} + +int get_user_cert(const char *keyname, char reply[REPLY_MAX]) +{ + return get_key(CERTS_DIR, keyname, USER_CERTIFICATE, reply); +} + +int get_user_key(const char *keyname, char reply[REPLY_MAX]) +{ + if(get_key(CERTS_DIR, keyname, USER_KEY, reply)) + return get_key(CERTS_DIR, keyname, USER_P12_CERT, reply); + return 0; } -int remove_cert(const char *certfile) +int remove_user_cert(const char *key) { - return remove_key(CERTS_DIR, certfile); + return remove_key(CERTS_DIR, key); } -int remove_userkey(const char *keyfile) +int remove_ca_cert(const char *key) { - return remove_key(USERKEYS_DIR, keyfile); + return remove_key(CACERTS_DIR, key); } diff --git a/cmds/keystore/keystore.c b/cmds/keystore/keystore.c index 5193b3d..df8d832 100644 --- a/cmds/keystore/keystore.c +++ b/cmds/keystore/keystore.c @@ -16,37 +16,89 @@ #include "keystore.h" +static inline int has_whitespace(char *name) +{ + if((strrchr(name, ' ') != NULL)) { + LOGE("'%s' contains whitespace character\n", name); + return 1; + } + return 0; +} + +static int do_list_user_certs(char **arg, char reply[REPLY_MAX]) +{ + return list_user_certs(reply); +} + +static int do_list_ca_certs(char **arg, char reply[REPLY_MAX]) +{ + return list_ca_certs(reply); +} + +static int do_install_user_cert(char **arg, char reply[REPLY_MAX]) +{ + if (has_whitespace(arg[0])) return -1; + /* copy the certificate and key to keystore */ + return install_user_cert(arg[0], arg[1], arg[2]); +} -static int do_list_certs(char **arg, char reply[REPLY_MAX]) +static int do_install_p12_cert(char **arg, char reply[REPLY_MAX]) { - return list_certs(reply); + if (has_whitespace(arg[0])) return -1; + return install_p12_cert(arg[0], arg[1]); } -static int do_list_userkeys(char **arg, char reply[REPLY_MAX]) +static int do_install_ca_cert(char **arg, char reply[REPLY_MAX]) { - return list_userkeys(reply); + if (has_whitespace(arg[0])) return -1; + /* copy the certificate and key to keystore */ + return install_ca_cert(arg[0], arg[1]); } -static int do_install_cert(char **arg, char reply[REPLY_MAX]) +static int do_add_ca_cert(char **arg, char reply[REPLY_MAX]) { - return install_cert(arg[0]); /* move the certificate to keystore */ + if (has_whitespace(arg[0])) return -1; + return add_ca_cert(arg[0], arg[1]); } -static int do_remove_cert(char **arg, char reply[REPLY_MAX]) +static int do_add_user_cert(char **arg, char reply[REPLY_MAX]) { - return remove_cert(arg[0]); /* certificate */ + if (has_whitespace(arg[0])) return -1; + return add_user_cert(arg[0], arg[1]); } -static int do_install_userkey(char **arg, char reply[REPLY_MAX]) +static int do_add_user_key(char **arg, char reply[REPLY_MAX]) { - return install_userkey(arg[0]); /* move the certificate to keystore */ + if (has_whitespace(arg[0])) return -1; + return add_user_key(arg[0], arg[1]); } -static int do_remove_userkey(char **arg, char reply[REPLY_MAX]) +static int do_get_ca_cert(char **arg, char reply[REPLY_MAX]) { - return remove_userkey(arg[0]); /* userkey */ + return get_ca_cert(arg[0], reply); } +static int do_get_user_cert(char **arg, char reply[REPLY_MAX]) +{ + return get_user_cert(arg[0], reply); +} + +static int do_get_user_key(char **arg, char reply[REPLY_MAX]) +{ + return get_user_key(arg[0], reply); +} + +static int do_remove_user_cert(char **arg, char reply[REPLY_MAX]) +{ + return remove_user_cert(arg[0]); +} + +static int do_remove_ca_cert(char **arg, char reply[REPLY_MAX]) +{ + return remove_ca_cert(arg[0]); +} + + struct cmdinfo { const char *name; unsigned numargs; @@ -55,12 +107,19 @@ struct cmdinfo { struct cmdinfo cmds[] = { - { "listcerts", 0, do_list_certs }, - { "listuserkeys", 0, do_list_userkeys }, - { "installcert", 1, do_install_cert }, - { "removecert", 1, do_remove_cert }, - { "installuserkey", 1, do_install_userkey }, - { "removeuserkey", 1, do_remove_userkey }, + { "listcacerts", 0, do_list_ca_certs }, + { "listusercerts", 0, do_list_user_certs }, + { "installusercert", 3, do_install_user_cert }, + { "installcacert", 2, do_install_ca_cert }, + { "installp12cert", 2, do_install_p12_cert }, + { "addusercert", 2, do_add_user_cert }, + { "adduserkey", 2, do_add_user_key }, + { "addcacert", 2, do_add_ca_cert }, + { "getusercert", 1, do_get_user_cert }, + { "getuserkey", 1, do_get_user_key }, + { "getcacert", 1, do_get_ca_cert }, + { "removecacert", 1, do_remove_ca_cert }, + { "removeusercert", 1, do_remove_user_cert }, }; static int readx(int s, void *_buf, int count) @@ -121,7 +180,7 @@ static int execute(int s, char cmd[BUFFER_MAX]) /* n is number of args (not counting arg[0]) */ arg[0] = cmd; while (*cmd) { - if (isspace(*cmd)) { + if (*cmd == CMD_DELIMITER) { *cmd++ = 0; n++; arg[n] = cmd; @@ -167,6 +226,7 @@ int shell_command(const int argc, const char **argv) int fd, i; short ret; unsigned short count; + char delimiter[2] = { CMD_DELIMITER, 0 }; char buf[BUFFER_MAX]=""; fd = socket_local_client(SOCKET_PATH, @@ -177,7 +237,7 @@ int shell_command(const int argc, const char **argv) exit(1); } for(i = 0; i < argc; i++) { - if (i > 0) strlcat(buf, " ", BUFFER_MAX); + if (i > 0) strlcat(buf, delimiter, BUFFER_MAX); if(strlcat(buf, argv[i], BUFFER_MAX) >= BUFFER_MAX) { fprintf(stderr, "Arguments are too long\n"); exit(1); diff --git a/cmds/keystore/keystore.h b/cmds/keystore/keystore.h index 35acf0b9..b9cb185 100644 --- a/cmds/keystore/keystore.h +++ b/cmds/keystore/keystore.h @@ -40,18 +40,35 @@ /* path of the keystore */ #define KEYSTORE_DIR_PREFIX "/data/misc/keystore" -#define CERTS_DIR KEYSTORE_DIR_PREFIX "/certs" -#define USERKEYS_DIR KEYSTORE_DIR_PREFIX "/userkeys" +#define CERTS_DIR KEYSTORE_DIR_PREFIX "/keys" +#define CACERTS_DIR KEYSTORE_DIR_PREFIX "/cacerts" +#define CA_CERTIFICATE "ca.crt" +#define USER_CERTIFICATE "user.crt" +#define USER_P12_CERT "user.p12" +#define USER_KEY "user.key" +#define DOT "." +#define DOTDOT ".." -#define BUFFER_MAX 1024 /* input buffer for commands */ +#define BUFFER_MAX 4096 /* input buffer for commands */ #define TOKEN_MAX 8 /* max number of arguments in buffer */ -#define REPLY_MAX 1024 /* largest reply allowed */ +#define REPLY_MAX 4096 /* largest reply allowed */ +#define CMD_DELIMITER '\t' #define KEYNAME_LENGTH 128 +#define IS_CONTENT 0 +#define IS_FILE 1 + /* commands.c */ -int list_certs(char reply[REPLY_MAX]); -int list_userkeys(char reply[REPLY_MAX]); -int install_cert(const char *certfile); -int install_userkey(const char *keyfile); -int remove_cert(const char *certfile); -int remove_userkey(const char *keyfile); +int list_ca_certs(char reply[REPLY_MAX]); +int list_user_certs(char reply[REPLY_MAX]); +int install_user_cert(const char *certname, const char *cert, const char *key); +int install_ca_cert(const char *certname, const char *cert); +int install_p12_cert(const char *certname, const char *cert); +int add_ca_cert(const char *certname, const char *content); +int add_user_cert(const char *certname, const char *content); +int add_user_key(const char *keyname, const char *content); +int get_ca_cert(const char *keyname, char reply[REPLY_MAX]); +int get_user_cert(const char *keyname, char reply[REPLY_MAX]); +int get_user_key(const char *keyname, char reply[REPLY_MAX]); +int remove_user_cert(const char *certname); +int remove_ca_cert(const char *certname); diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 7fb3449..ca9632a 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -23,6 +23,7 @@ import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; +import android.content.IIntentSender; import android.content.SharedPreferences; import android.content.pm.ActivityInfo; import android.content.res.Configuration; @@ -863,13 +864,26 @@ public class Activity extends ContextThemeWrapper final Integer dialogId = ids[i]; Bundle dialogState = b.getBundle(savedDialogKeyFor(dialogId)); if (dialogState != null) { - final Dialog dialog = onCreateDialog(dialogId); - dialog.onRestoreInstanceState(dialogState); + // Calling onRestoreInstanceState() below will invoke dispatchOnCreate + // so tell createDialog() not to do it, otherwise we get an exception + final Dialog dialog = createDialog(dialogId, false); mManagedDialogs.put(dialogId, dialog); + onPrepareDialog(dialogId, dialog); + dialog.onRestoreInstanceState(dialogState); } } } + private Dialog createDialog(Integer dialogId, boolean dispatchOnCreate) { + final Dialog dialog = onCreateDialog(dialogId); + if (dialog == null) { + throw new IllegalArgumentException("Activity#onCreateDialog did " + + "not create a dialog for id " + dialogId); + } + if (dispatchOnCreate) dialog.dispatchOnCreate(null); + return dialog; + } + private String savedDialogKeyFor(int key) { return SAVED_DIALOG_KEY_PREFIX + key; } @@ -2418,12 +2432,7 @@ public class Activity extends ContextThemeWrapper } Dialog dialog = mManagedDialogs.get(id); if (dialog == null) { - dialog = onCreateDialog(id); - if (dialog == null) { - throw new IllegalArgumentException("Activity#onCreateDialog did " - + "not create a dialog for id " + id); - } - dialog.dispatchOnCreate(null); + dialog = createDialog(id, true); mManagedDialogs.put(id, dialog); } diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java index 3d3d7d5..b6f855a 100644 --- a/core/java/android/app/ActivityManagerNative.java +++ b/core/java/android/app/ActivityManagerNative.java @@ -17,9 +17,10 @@ package android.app; import android.content.ComponentName; -import android.content.ContentResolver; import android.content.Intent; import android.content.IntentFilter; +import android.content.IIntentSender; +import android.content.IIntentReceiver; import android.content.pm.ApplicationInfo; import android.content.pm.ConfigurationInfo; import android.content.pm.IPackageDataObserver; diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index d08fc11..98bd45a 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -23,6 +23,7 @@ import android.content.ContentProvider; import android.content.Context; import android.content.IContentProvider; import android.content.Intent; +import android.content.IIntentReceiver; import android.content.ServiceConnection; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; @@ -31,6 +32,7 @@ import android.content.pm.InstrumentationInfo; import android.content.pm.PackageManager; import android.content.pm.ProviderInfo; import android.content.pm.ServiceInfo; +import android.content.pm.PackageParser.Component; import android.content.res.AssetManager; import android.content.res.Configuration; import android.content.res.Resources; @@ -185,7 +187,7 @@ public final class ActivityThread { try { appInfo = getPackageManager().getApplicationInfo( pkgInfo.getPackageName(), - PackageManager.GET_SUPPORTS_DENSITIES | PackageManager.GET_EXPANDABLE); + PackageManager.GET_SUPPORTS_DENSITIES); } catch (RemoteException e) { throw new AssertionError(e); } @@ -285,6 +287,10 @@ public final class ActivityThread { return mPackageName; } + public ApplicationInfo getApplicationInfo() { + return mApplicationInfo; + } + public boolean isSecurityViolation() { return mSecurityViolation; } @@ -2082,6 +2088,10 @@ public final class ActivityThread { return mInitialApplication; } + public String getProcessName() { + return mBoundApplication.processName; + } + public ApplicationContext getSystemContext() { synchronized (this) { if (mSystemContext == null) { @@ -3216,7 +3226,7 @@ public final class ActivityThread { r.activity.getComponentName().getClassName()); if (!r.activity.mCalled) { throw new SuperNotCalledException( - "Activity " + r.intent.getComponent().toShortString() + "Activity " + safeToComponentShortString(r.intent) + " did not call through to super.onPause()"); } } catch (SuperNotCalledException e) { @@ -3225,7 +3235,7 @@ public final class ActivityThread { if (!mInstrumentation.onException(r.activity, e)) { throw new RuntimeException( "Unable to pause activity " - + r.intent.getComponent().toShortString() + + safeToComponentShortString(r.intent) + ": " + e.toString(), e); } } @@ -3240,7 +3250,7 @@ public final class ActivityThread { if (!mInstrumentation.onException(r.activity, e)) { throw new RuntimeException( "Unable to stop activity " - + r.intent.getComponent().toShortString() + + safeToComponentShortString(r.intent) + ": " + e.toString(), e); } } @@ -3265,7 +3275,7 @@ public final class ActivityThread { if (!mInstrumentation.onException(r.activity, e)) { throw new RuntimeException( "Unable to retain child activities " - + r.intent.getComponent().toShortString() + + safeToComponentShortString(r.intent) + ": " + e.toString(), e); } } @@ -3276,7 +3286,7 @@ public final class ActivityThread { r.activity.onDestroy(); if (!r.activity.mCalled) { throw new SuperNotCalledException( - "Activity " + r.intent.getComponent().toShortString() + + "Activity " + safeToComponentShortString(r.intent) + " did not call through to super.onDestroy()"); } if (r.window != null) { @@ -3287,8 +3297,7 @@ public final class ActivityThread { } catch (Exception e) { if (!mInstrumentation.onException(r.activity, e)) { throw new RuntimeException( - "Unable to destroy activity " - + r.intent.getComponent().toShortString() + "Unable to destroy activity " + safeToComponentShortString(r.intent) + ": " + e.toString(), e); } } @@ -3298,6 +3307,11 @@ public final class ActivityThread { return r; } + private static String safeToComponentShortString(Intent intent) { + ComponentName component = intent.getComponent(); + return component == null ? "[Unknown]" : component.toShortString(); + } + private final void handleDestroyActivity(IBinder token, boolean finishing, int configChanges, boolean getNonConfigInstance) { ActivityRecord r = performDestroyActivity(token, finishing, diff --git a/core/java/android/app/ApplicationContext.java b/core/java/android/app/ApplicationContext.java index c261acb..1666588 100644 --- a/core/java/android/app/ApplicationContext.java +++ b/core/java/android/app/ApplicationContext.java @@ -32,6 +32,8 @@ import android.content.ContextWrapper; import android.content.IContentProvider; import android.content.Intent; import android.content.IntentFilter; +import android.content.IIntentReceiver; +import android.content.IntentSender; import android.content.ReceiverCallNotAllowedException; import android.content.ServiceConnection; import android.content.SharedPreferences; @@ -284,6 +286,14 @@ class ApplicationContext extends Context { } @Override + public ApplicationInfo getApplicationInfo() { + if (mPackageInfo != null) { + return mPackageInfo.getApplicationInfo(); + } + throw new RuntimeException("Not supported in system context"); + } + + @Override public String getPackageResourcePath() { if (mPackageInfo != null) { return mPackageInfo.getResDir(); @@ -1533,14 +1543,16 @@ class ApplicationContext extends Context { // overall package (such as if it has multiple launcher entries). Intent intentToResolve = new Intent(Intent.ACTION_MAIN); intentToResolve.addCategory(Intent.CATEGORY_INFO); - ResolveInfo resolveInfo = resolveActivity(intentToResolve, 0, packageName); + intentToResolve.setPackage(packageName); + ResolveInfo resolveInfo = resolveActivity(intentToResolve, 0); // Otherwise, try to find a main launcher activity. if (resolveInfo == null) { // reuse the intent instance intentToResolve.removeCategory(Intent.CATEGORY_INFO); intentToResolve.addCategory(Intent.CATEGORY_LAUNCHER); - resolveInfo = resolveActivity(intentToResolve, 0, packageName); + intentToResolve.setPackage(packageName); + resolveInfo = resolveActivity(intentToResolve, 0); } if (resolveInfo == null) { return null; @@ -1788,19 +1800,6 @@ class ApplicationContext extends Context { } @Override - public ResolveInfo resolveActivity(Intent intent, int flags, String packageName) { - try { - return mPM.resolveIntentForPackage( - intent, - intent.resolveTypeIfNeeded(mContext.getContentResolver()), - flags, - packageName); - } catch (RemoteException e) { - throw new RuntimeException("Package manager has died", e); - } - } - - @Override public List<ResolveInfo> queryIntentActivities(Intent intent, int flags) { try { @@ -2373,11 +2372,11 @@ class ApplicationContext extends Context { // Should never happen! } } - + @Override - public void freeStorage(long idealStorageSize, PendingIntent opFinishedIntent) { + public void freeStorage(long freeStorageSize, IntentSender pi) { try { - mPM.freeStorage(idealStorageSize, opFinishedIntent); + mPM.freeStorage(freeStorageSize, pi); } catch (RemoteException e) { // Should never happen! } diff --git a/core/java/android/app/ApplicationErrorReport.java b/core/java/android/app/ApplicationErrorReport.java index 0c8f95d..6b17236 100644 --- a/core/java/android/app/ApplicationErrorReport.java +++ b/core/java/android/app/ApplicationErrorReport.java @@ -151,6 +151,11 @@ public class ApplicationErrorReport implements Parcelable { public String exceptionClassName; /** + * Message stored in the exception. + */ + public String exceptionMessage; + + /** * File which the exception was thrown from. */ public String throwFileName; @@ -181,6 +186,7 @@ public class ApplicationErrorReport implements Parcelable { */ public CrashInfo(Parcel in) { exceptionClassName = in.readString(); + exceptionMessage = in.readString(); throwFileName = in.readString(); throwClassName = in.readString(); throwMethodName = in.readString(); @@ -192,6 +198,7 @@ public class ApplicationErrorReport implements Parcelable { */ public void writeToParcel(Parcel dest, int flags) { dest.writeString(exceptionClassName); + dest.writeString(exceptionMessage); dest.writeString(throwFileName); dest.writeString(throwClassName); dest.writeString(throwMethodName); @@ -203,6 +210,7 @@ public class ApplicationErrorReport implements Parcelable { */ public void dump(Printer pw, String prefix) { pw.println(prefix + "exceptionClassName: " + exceptionClassName); + pw.println(prefix + "exceptionMessage: " + exceptionMessage); pw.println(prefix + "throwFileName: " + throwFileName); pw.println(prefix + "throwClassName: " + throwClassName); pw.println(prefix + "throwMethodName: " + throwMethodName); diff --git a/core/java/android/app/ApplicationThreadNative.java b/core/java/android/app/ApplicationThreadNative.java index 6750d12..4b64c94 100644 --- a/core/java/android/app/ApplicationThreadNative.java +++ b/core/java/android/app/ApplicationThreadNative.java @@ -18,6 +18,7 @@ package android.app; import android.content.ComponentName; import android.content.Intent; +import android.content.IIntentReceiver; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.ProviderInfo; diff --git a/core/java/android/app/BackupAgent.java b/core/java/android/app/BackupAgent.java index 997bfdc..85c001c 100644 --- a/core/java/android/app/BackupAgent.java +++ b/core/java/android/app/BackupAgent.java @@ -17,6 +17,7 @@ package android.app; import android.app.IBackupAgent; +import android.backup.BackupDataInput; import android.backup.BackupDataOutput; import android.content.Context; import android.content.ContextWrapper; @@ -25,6 +26,8 @@ import android.os.ParcelFileDescriptor; import android.os.RemoteException; import android.util.Log; +import java.io.IOException; + /** * This is the central interface between an application and Android's * settings backup mechanism. @@ -32,6 +35,8 @@ import android.util.Log; * @hide pending API solidification */ public abstract class BackupAgent extends ContextWrapper { + private static final String TAG = "BackupAgent"; + public BackupAgent() { super(null); } @@ -77,8 +82,8 @@ public abstract class BackupAgent extends ContextWrapper { * file. The application should record the final backup state * here after restoring its data from dataFd. */ - public abstract void onRestore(ParcelFileDescriptor /* TODO: BackupDataInput */ data, - ParcelFileDescriptor newState); + public abstract void onRestore(BackupDataInput data, ParcelFileDescriptor newState) + throws IOException; // ----- Core implementation ----- @@ -107,13 +112,11 @@ public abstract class BackupAgent extends ContextWrapper { ParcelFileDescriptor newState) throws RemoteException { // !!! TODO - real implementation; for now just invoke the callbacks directly Log.v(TAG, "doBackup() invoked"); - BackupDataOutput output = new BackupDataOutput(BackupAgent.this, - data.getFileDescriptor()); + BackupDataOutput output = new BackupDataOutput(data.getFileDescriptor()); try { BackupAgent.this.onBackup(oldState, output, newState); } catch (RuntimeException ex) { - Log.d("BackupAgent", "onBackup (" - + BackupAgent.this.getClass().getName() + ") threw", ex); + Log.d(TAG, "onBackup (" + BackupAgent.this.getClass().getName() + ") threw", ex); throw ex; } } @@ -122,7 +125,16 @@ public abstract class BackupAgent extends ContextWrapper { ParcelFileDescriptor newState) throws RemoteException { // !!! TODO - real implementation; for now just invoke the callbacks directly Log.v(TAG, "doRestore() invoked"); - BackupAgent.this.onRestore(data, newState); + BackupDataInput input = new BackupDataInput(data.getFileDescriptor()); + try { + BackupAgent.this.onRestore(input, newState); + } catch (IOException ex) { + Log.d(TAG, "onRestore (" + BackupAgent.this.getClass().getName() + ") threw", ex); + throw new RuntimeException(ex); + } catch (RuntimeException ex) { + Log.d(TAG, "onRestore (" + BackupAgent.this.getClass().getName() + ") threw", ex); + throw ex; + } } } } diff --git a/core/java/android/app/FullBackupAgent.java b/core/java/android/app/FullBackupAgent.java index bf5cb5d..89becf4 100644 --- a/core/java/android/app/FullBackupAgent.java +++ b/core/java/android/app/FullBackupAgent.java @@ -1,5 +1,6 @@ package android.app; +import android.backup.BackupDataInput; import android.backup.BackupDataOutput; import android.backup.FileBackupHelper; import android.os.ParcelFileDescriptor; @@ -52,6 +53,6 @@ public class FullBackupAgent extends BackupAgent { } @Override - public void onRestore(ParcelFileDescriptor data, ParcelFileDescriptor newState) { + public void onRestore(BackupDataInput data, ParcelFileDescriptor newState) { } } diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java index c948aec..66bc85b 100644 --- a/core/java/android/app/IActivityManager.java +++ b/core/java/android/app/IActivityManager.java @@ -21,6 +21,8 @@ import android.content.ContentProviderNative; import android.content.IContentProvider; import android.content.Intent; import android.content.IntentFilter; +import android.content.IIntentSender; +import android.content.IIntentReceiver; import android.content.pm.ApplicationInfo; import android.content.pm.ConfigurationInfo; import android.content.pm.IPackageDataObserver; diff --git a/core/java/android/app/IApplicationThread.java b/core/java/android/app/IApplicationThread.java index bca1fea..029c650 100644 --- a/core/java/android/app/IApplicationThread.java +++ b/core/java/android/app/IApplicationThread.java @@ -18,6 +18,7 @@ package android.app; import android.content.ComponentName; import android.content.Intent; +import android.content.IIntentReceiver; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.ProviderInfo; diff --git a/core/java/android/app/IIntentReceiver.aidl b/core/java/android/app/IIntentReceiver.aidl deleted file mode 100755 index 5f5d0eb..0000000 --- a/core/java/android/app/IIntentReceiver.aidl +++ /dev/null @@ -1,33 +0,0 @@ -/* -** -** Copyright 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 android.app; - -import android.content.Intent; -import android.os.Bundle; - -/** - * System private API for dispatching intent broadcasts. This is given to the - * activity manager as part of registering for an intent broadcasts, and is - * called when it receives intents. - * - * {@hide} - */ -oneway interface IIntentReceiver { - void performReceive(in Intent intent, int resultCode, - String data, in Bundle extras, boolean ordered); -} - diff --git a/core/java/android/app/IIntentSender.aidl b/core/java/android/app/IIntentSender.aidl deleted file mode 100644 index 53e135a..0000000 --- a/core/java/android/app/IIntentSender.aidl +++ /dev/null @@ -1,27 +0,0 @@ -/* //device/java/android/android/app/IActivityPendingResult.aidl -** -** Copyright 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.app; - -import android.app.IIntentReceiver; -import android.content.Intent; - -/** @hide */ -interface IIntentSender { - int send(int code, in Intent intent, String resolvedType, - IIntentReceiver finishedReceiver); -} diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java index f6a28b2..e31f4f8 100644 --- a/core/java/android/app/Instrumentation.java +++ b/core/java/android/app/Instrumentation.java @@ -446,13 +446,13 @@ public class Instrumentation { if (ai == null) { throw new RuntimeException("Unable to resolve activity for: " + intent); } - if (!ai.applicationInfo.processName.equals( - getTargetContext().getPackageName())) { + String myProc = mThread.getProcessName(); + if (!ai.processName.equals(myProc)) { // todo: if this intent is ambiguous, look here to see if // there is a single match that is in our package. - throw new RuntimeException("Intent resolved to different package " - + ai.applicationInfo.packageName + ": " - + intent); + throw new RuntimeException("Intent in process " + + myProc + " resolved to different process " + + ai.processName + ": " + intent); } intent.setComponent(new ComponentName( diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java index cb660c7..f9c38f9 100644 --- a/core/java/android/app/PendingIntent.java +++ b/core/java/android/app/PendingIntent.java @@ -18,6 +18,9 @@ package android.app; import android.content.Context; import android.content.Intent; +import android.content.IIntentReceiver; +import android.content.IIntentSender; +import android.content.IntentSender; import android.os.Bundle; import android.os.RemoteException; import android.os.Handler; @@ -105,7 +108,7 @@ public final class PendingIntent implements Parcelable { public CanceledException(Exception cause) { super(cause); } - }; + } /** * Callback interface for discovering when a send operation has @@ -270,6 +273,21 @@ public final class PendingIntent implements Parcelable { return null; } + private class IntentSenderWrapper extends IntentSender { + protected IntentSenderWrapper(IIntentSender target) { + super(target); + } + } + /** + * Retrieve a IntentSender object that wraps the existing sender of the PendingIntent + * + * @return Returns a IntentSender object that wraps the sender of PendingIntent + * + */ + public IntentSender getIntentSender() { + return new IntentSenderWrapper(mTarget); + } + /** * Cancel a currently active PendingIntent. Only the original application * owning an PendingIntent can cancel it. diff --git a/core/java/android/app/SearchDialog.java b/core/java/android/app/SearchDialog.java index 9141c4c..3a3a983 100644 --- a/core/java/android/app/SearchDialog.java +++ b/core/java/android/app/SearchDialog.java @@ -35,11 +35,6 @@ import android.content.res.Resources; import android.database.Cursor; import android.graphics.drawable.AnimationDrawable; import android.graphics.drawable.Drawable; -import android.location.Criteria; -import android.location.Location; -import android.location.LocationListener; -import android.location.LocationManager; -import android.location.LocationProvider; import android.net.Uri; import android.os.Bundle; import android.os.SystemClock; @@ -139,9 +134,7 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS private SuggestionsAdapter mSuggestionsAdapter; // Whether to rewrite queries when selecting suggestions - // TODO: This is disabled because of problems with persistent selections - // causing non-user-initiated rewrites. - private static final boolean REWRITE_QUERIES = false; + private static final boolean REWRITE_QUERIES = true; // The query entered by the user. This is not changed when selecting a suggestion // that modifies the contents of the text field. But if the user then edits @@ -153,15 +146,6 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS private final WeakHashMap<String, Drawable> mOutsideDrawablesCache = new WeakHashMap<String, Drawable>(); - // Objects we keep around for requesting location updates when the dialog is started - // (and canceling them when the dialog is stopped). We don't actually make use of the - // updates ourselves here, so the LocationListener is just a dummy which doesn't do - // anything. We only do this here so that other suggest providers which wish to provide - // location-based suggestions are more likely to get a good fresh location. - private LocationManager mLocationManager; - private LocationProvider mLocationProvider; - private LocationListener mDummyLocationListener; - /** * Constructor - fires it up and makes it look like the search UI. * @@ -240,37 +224,6 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS mVoiceAppSearchIntent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH); mVoiceAppSearchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - - mLocationManager = - (LocationManager) getContext().getSystemService(Context.LOCATION_SERVICE); - - if (mLocationManager != null) { - Criteria criteria = new Criteria(); - criteria.setAccuracy(Criteria.ACCURACY_COARSE); - - String providerName = mLocationManager.getBestProvider(criteria, true); - - if (providerName != null) { - mLocationProvider = mLocationManager.getProvider(providerName); - } - - // Just a dumb listener that doesn't do anything - requesting location updates here - // is only intended to give location-based suggestion providers the best chance - // of getting a good fresh location. - mDummyLocationListener = new LocationListener() { - public void onLocationChanged(Location location) { - } - - public void onProviderDisabled(String provider) { - } - - public void onProviderEnabled(String provider) { - } - - public void onStatusChanged(String provider, int status, Bundle extras) { - } - }; - } } /** @@ -423,8 +376,6 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS // receive broadcasts getContext().registerReceiver(mBroadcastReceiver, mCloseDialogsFilter); getContext().registerReceiver(mBroadcastReceiver, mPackageFilter); - - startLocationUpdates(); } /** @@ -437,8 +388,6 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS public void onStop() { super.onStop(); - stopLocationUpdates(); - // stop receiving broadcasts (throws exception if none registered) try { getContext().unregisterReceiver(mBroadcastReceiver); @@ -456,26 +405,7 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS mUserQuery = null; mPreviousComponents = null; } - - /** - * Asks the LocationManager for location updates so that it goes and gets a fresh location - * if needed. - */ - private void startLocationUpdates() { - if (mLocationManager != null && mLocationProvider != null) { - mLocationManager.requestLocationUpdates(mLocationProvider.getName(), - 0, 0, mDummyLocationListener, getContext().getMainLooper()); - } - } - - /** - * Makes sure to stop listening for location updates to save battery. - */ - private void stopLocationUpdates() { - mLocationManager.removeUpdates(mDummyLocationListener); - } - /** * Sets the search dialog to the 'working' state, which shows a working spinner in the * right hand size of the text field. @@ -1168,7 +1098,7 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS */ protected void launchQuerySearch(int actionKey, String actionMsg) { String query = mSearchAutoComplete.getText().toString(); - Intent intent = createIntent(Intent.ACTION_SEARCH, null, query, null, + Intent intent = createIntent(Intent.ACTION_SEARCH, null, null, query, null, actionKey, actionMsg); launchIntent(intent); } @@ -1238,8 +1168,8 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS // logic for falling back on the searchable default cv.put(SearchManager.SUGGEST_COLUMN_INTENT_ACTION, intent.getAction()); cv.put(SearchManager.SUGGEST_COLUMN_INTENT_DATA, intent.getDataString()); - cv.put(SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_DATA, - intent.getStringExtra(SearchManager.EXTRA_DATA_KEY)); + cv.put(SearchManager.SUGGEST_COLUMN_INTENT_COMPONENT_NAME, + intent.getStringExtra(SearchManager.COMPONENT_NAME_KEY)); // ensure the icons will work for global search cv.put(SearchManager.SUGGEST_COLUMN_ICON_1, @@ -1467,11 +1397,14 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS } Uri dataUri = (data == null) ? null : Uri.parse(data); - String extraData = getColumnString(c, SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_DATA); + String componentName = getColumnString( + c, SearchManager.SUGGEST_COLUMN_INTENT_COMPONENT_NAME); String query = getColumnString(c, SearchManager.SUGGEST_COLUMN_QUERY); + String extraData = getColumnString(c, SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_DATA); - return createIntent(action, dataUri, query, extraData, actionKey, actionMsg); + return createIntent(action, dataUri, extraData, query, componentName, actionKey, + actionMsg); } catch (RuntimeException e ) { int rowNum; try { // be really paranoid now @@ -1490,16 +1423,17 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS * * @param action Intent action. * @param data Intent data, or <code>null</code>. - * @param query Intent query, or <code>null</code>. * @param extraData Data for {@link SearchManager#EXTRA_DATA_KEY} or <code>null</code>. + * @param query Intent query, or <code>null</code>. + * @param componentName Data for {@link SearchManager#COMPONENT_NAME_KEY} or <code>null</code>. * @param actionKey The key code of the action key that was pressed, * or {@link KeyEvent#KEYCODE_UNKNOWN} if none. * @param actionMsg The message for the action key that was pressed, * or <code>null</code> if none. * @return The intent. */ - private Intent createIntent(String action, Uri data, String query, String extraData, - int actionKey, String actionMsg) { + private Intent createIntent(String action, Uri data, String extraData, String query, + String componentName, int actionKey, String actionMsg) { // Now build the Intent Intent intent = new Intent(action); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); @@ -1512,6 +1446,9 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS if (extraData != null) { intent.putExtra(SearchManager.EXTRA_DATA_KEY, extraData); } + if (componentName != null) { + intent.putExtra(SearchManager.COMPONENT_NAME_KEY, componentName); + } if (mAppSearchData != null) { intent.putExtra(SearchManager.APP_DATA, mAppSearchData); } diff --git a/core/java/android/app/SearchManager.java b/core/java/android/app/SearchManager.java index 1ddd20a..eb80400 100644 --- a/core/java/android/app/SearchManager.java +++ b/core/java/android/app/SearchManager.java @@ -1165,6 +1165,14 @@ public class SearchManager public final static String ACTION_KEY = "action_key"; /** + * Intent component name key: This key will be used for the extra populated by the + * {@link #SUGGEST_COLUMN_INTENT_COMPONENT_NAME} column. + * + * {@hide} + */ + public final static String COMPONENT_NAME_KEY = "intent_component_name_key"; + + /** * Intent extra data key: This key will be used for the extra populated by the * {@link #SUGGEST_COLUMN_INTENT_EXTRA_DATA} column. * @@ -1364,14 +1372,24 @@ public class SearchManager */ public final static String SUGGEST_COLUMN_INTENT_DATA = "suggest_intent_data"; /** + * Column name for suggestions cursor. <i>Optional.</i> If this column exists <i>and</i> + * this element exists at the given row, this is the data that will be used when + * forming the suggestion's intent. If not provided, the Intent's extra data field will be null. + * This column allows suggestions to provide additional arbitrary data which will be included as + * an extra under the key EXTRA_DATA_KEY. + * + * @hide Pending API council approval. + */ + public final static String SUGGEST_COLUMN_INTENT_EXTRA_DATA = "suggest_intent_extra_data"; + /** * Column name for suggestions cursor. <i>Optional.</i> This column allows suggestions * to provide additional arbitrary data which will be included as an extra under the key - * {@link #EXTRA_DATA_KEY}. For use by the global search system only - if other providers + * {@link #COMPONENT_NAME_KEY}. For use by the global search system only - if other providers * attempt to use this column, the value will be overwritten by global search. * * @hide */ - public final static String SUGGEST_COLUMN_INTENT_EXTRA_DATA = "suggest_intent_extra_data"; + public final static String SUGGEST_COLUMN_INTENT_COMPONENT_NAME = "suggest_intent_component"; /** * Column name for suggestions cursor. <i>Optional.</i> If this column exists <i>and</i> * this element exists at the given row, then "/" and this value will be appended to the data diff --git a/core/java/android/backup/BackupDataInput.java b/core/java/android/backup/BackupDataInput.java index 609dd90..69c206c 100644 --- a/core/java/android/backup/BackupDataInput.java +++ b/core/java/android/backup/BackupDataInput.java @@ -82,9 +82,9 @@ public class BackupDataInput { } } - public int readEntityData(byte[] data, int size) throws IOException { + public int readEntityData(byte[] data, int offset, int size) throws IOException { if (mHeaderReady) { - int result = readEntityData_native(mBackupReader, data, size); + int result = readEntityData_native(mBackupReader, data, offset, size); if (result >= 0) { return result; } else { @@ -95,9 +95,23 @@ public class BackupDataInput { } } + public void skipEntityData() throws IOException { + if (mHeaderReady) { + int result = skipEntityData_native(mBackupReader); + if (result >= 0) { + return; + } else { + throw new IOException("result=0x" + Integer.toHexString(result)); + } + } else { + throw new IllegalStateException("mHeaderReady=false"); + } + } + private native static int ctor(FileDescriptor fd); private native static void dtor(int mBackupReader); private native int readNextHeader_native(int mBackupReader, EntityHeader entity); - private native int readEntityData_native(int mBackupReader, byte[] data, int size); + private native int readEntityData_native(int mBackupReader, byte[] data, int offset, int size); + private native int skipEntityData_native(int mBackupReader); } diff --git a/core/java/android/backup/BackupDataInputStream.java b/core/java/android/backup/BackupDataInputStream.java new file mode 100644 index 0000000..b705c4c --- /dev/null +++ b/core/java/android/backup/BackupDataInputStream.java @@ -0,0 +1,63 @@ +/* + * 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.backup; + +import android.util.Log; + +import java.io.InputStream; +import java.io.IOException; + +/** @hide */ +public class BackupDataInputStream extends InputStream { + + String key; + int dataSize; + + BackupDataInput mData; + byte[] mOneByte; + + BackupDataInputStream(BackupDataInput data) { + mData = data; + } + + public int read() throws IOException { + byte[] one = mOneByte; + if (mOneByte == null) { + one = mOneByte = new byte[1]; + } + mData.readEntityData(one, 0, 1); + return one[0]; + } + + public int read(byte[] b, int offset, int size) throws IOException { + return mData.readEntityData(b, offset, size); + } + + public int read(byte[] b) throws IOException { + return mData.readEntityData(b, 0, b.length); + } + + public String getKey() { + return this.key; + } + + public int size() { + return this.dataSize; + } +} + + diff --git a/core/java/android/backup/BackupDataOutput.java b/core/java/android/backup/BackupDataOutput.java index 1348d81..05e667e 100644 --- a/core/java/android/backup/BackupDataOutput.java +++ b/core/java/android/backup/BackupDataOutput.java @@ -24,13 +24,11 @@ import java.io.IOException; /** @hide */ public class BackupDataOutput { int mBackupWriter; - private Context mContext; public static final int OP_UPDATE = 1; public static final int OP_DELETE = 2; - public BackupDataOutput(Context context, FileDescriptor fd) { - mContext = context; + public BackupDataOutput(FileDescriptor fd) { if (fd == null) throw new NullPointerException(); mBackupWriter = ctor(fd); if (mBackupWriter == 0) { @@ -38,6 +36,7 @@ public class BackupDataOutput { } } + // A dataSize of -1 indicates that the record under this key should be deleted public int writeEntityHeader(String key, int dataSize) throws IOException { int result = writeEntityHeader_native(mBackupWriter, key, dataSize); if (result >= 0) { diff --git a/core/java/android/backup/FileRestoreHelper.java b/core/java/android/backup/FileRestoreHelper.java new file mode 100644 index 0000000..b7e3625 --- /dev/null +++ b/core/java/android/backup/FileRestoreHelper.java @@ -0,0 +1,41 @@ +/* + * 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.backup; + +import android.content.Context; +import android.util.Log; + +import java.io.File; + +/** @hide */ +public class FileRestoreHelper extends RestoreHelperBase implements RestoreHelper { + private static final String TAG = "FileRestoreHelper"; + + File mFilesDir; + + public FileRestoreHelper(Context context) { + super(context); + mFilesDir = context.getFilesDir(); + } + + public void restoreEntity(BackupDataInputStream data) { + Log.d(TAG, "got entity '" + data.getKey() + "' size=" + data.size()); // TODO: turn this off before ship + File f = new File(mFilesDir, data.getKey()); + writeFile(f, data); + } +} + diff --git a/core/java/android/backup/RestoreHelper.java b/core/java/android/backup/RestoreHelper.java index ebd9906..e47869c 100644 --- a/core/java/android/backup/RestoreHelper.java +++ b/core/java/android/backup/RestoreHelper.java @@ -16,8 +16,19 @@ package android.backup; +import android.os.ParcelFileDescriptor; + +import java.io.InputStream; + /** @hide */ public interface RestoreHelper { - public void performRestore(); + /** + * Called by RestoreHelperDispatcher to dispatch one entity of data. + * <p class=note> + * Do not close the <code>data</code> stream. Do not read more than + * <code>dataSize</code> bytes from <code>data</code>. + */ + public void restoreEntity(BackupDataInputStream data); + public void writeSnapshot(ParcelFileDescriptor fd); } diff --git a/core/java/android/backup/RestoreHelperBase.java b/core/java/android/backup/RestoreHelperBase.java new file mode 100644 index 0000000..93a8fef --- /dev/null +++ b/core/java/android/backup/RestoreHelperBase.java @@ -0,0 +1,82 @@ +/* + * 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.backup; + +import android.content.Context; +import android.os.ParcelFileDescriptor; +import android.util.Log; + +import java.io.InputStream; +import java.io.File; +import java.io.FileDescriptor; +import java.io.FileOutputStream; + +class RestoreHelperBase { + private static final String TAG = "RestoreHelperBase"; + + int mPtr; + Context mContext; + boolean mExceptionLogged; + + RestoreHelperBase(Context context) { + mPtr = ctor(); + mContext = context; + } + + protected void finalize() throws Throwable { + try { + dtor(mPtr); + } finally { + super.finalize(); + } + } + + void writeFile(File f, InputStream in) { + if (!(in instanceof BackupDataInputStream)) { + throw new IllegalStateException("input stream must be a BackupDataInputStream"); + } + int result = -1; + + // Create the enclosing directory. + File parent = f.getParentFile(); + parent.mkdirs(); + + result = writeFile_native(mPtr, f.getAbsolutePath(), + ((BackupDataInputStream)in).mData.mBackupReader); + if (result != 0) { + // Bail on this entity. Only log one failure per helper object. + if (!mExceptionLogged) { + Log.e(TAG, "Failed restoring file '" + f + "' for app '" + + mContext.getPackageName() + "\' result=0x" + + Integer.toHexString(result)); + mExceptionLogged = true; + } + } + } + + public void writeSnapshot(ParcelFileDescriptor fd) { + int result = writeSnapshot_native(mPtr, fd.getFileDescriptor()); + // TODO: Do something with the error. + } + + private static native int ctor(); + private static native void dtor(int ptr); + private static native int writeFile_native(int ptr, String filename, int backupReader); + private static native int writeSnapshot_native(int ptr, FileDescriptor fd); +} + + diff --git a/core/java/android/backup/RestoreHelperDispatcher.java b/core/java/android/backup/RestoreHelperDispatcher.java new file mode 100644 index 0000000..4861775 --- /dev/null +++ b/core/java/android/backup/RestoreHelperDispatcher.java @@ -0,0 +1,78 @@ +/* + * 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.backup; + +import android.os.ParcelFileDescriptor; +import android.util.Log; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +/** @hide */ +public class RestoreHelperDispatcher { + private static final String TAG = "RestoreHelperDispatcher"; + + HashMap<String,RestoreHelper> mHelpers = new HashMap<String,RestoreHelper>(); + + public void addHelper(String keyPrefix, RestoreHelper helper) { + mHelpers.put(keyPrefix, helper); + } + + public void dispatch(BackupDataInput input, ParcelFileDescriptor newState) throws IOException { + boolean alreadyComplained = false; + + BackupDataInputStream stream = new BackupDataInputStream(input); + while (input.readNextHeader()) { + + String rawKey = input.getKey(); + int pos = rawKey.indexOf(':'); + if (pos > 0) { + String prefix = rawKey.substring(0, pos); + RestoreHelper helper = mHelpers.get(prefix); + if (helper != null) { + stream.dataSize = input.getDataSize(); + stream.key = rawKey.substring(pos+1); + helper.restoreEntity(stream); + } else { + if (!alreadyComplained) { + Log.w(TAG, "Couldn't find helper for: '" + rawKey + "'"); + alreadyComplained = true; + } + } + } else { + if (!alreadyComplained) { + Log.w(TAG, "Entity with no prefix: '" + rawKey + "'"); + alreadyComplained = true; + } + } + input.skipEntityData(); // In case they didn't consume the data. + } + + if (mHelpers.size() > 1) { + throw new RuntimeException("RestoreHelperDispatcher won't get your your" + + " data in the right order yet."); + } + + // Write out the state files + for (RestoreHelper helper: mHelpers.values()) { + // TODO: Write a header for the state + helper.writeSnapshot(newState); + } + } +} + diff --git a/core/java/android/backup/RestoreSet.java b/core/java/android/backup/RestoreSet.java index 7f09af3..96a99ae 100644 --- a/core/java/android/backup/RestoreSet.java +++ b/core/java/android/backup/RestoreSet.java @@ -46,11 +46,11 @@ public class RestoreSet implements Parcelable { public int token; - RestoreSet() { + public RestoreSet() { // Leave everything zero / null } - RestoreSet(String _name, String _dev, int _token) { + public RestoreSet(String _name, String _dev, int _token) { name = _name; device = _dev; token = _token; diff --git a/core/java/android/bluetooth/BluetoothHeadset.java b/core/java/android/bluetooth/BluetoothHeadset.java index e198435..fe1e09a 100644 --- a/core/java/android/bluetooth/BluetoothHeadset.java +++ b/core/java/android/bluetooth/BluetoothHeadset.java @@ -332,6 +332,31 @@ public class BluetoothHeadset { } /** + * Get battery usage hint for Bluetooth Headset service. + * This is a monotonically increasing integer. Wraps to 0 at + * Integer.MAX_INT, and at boot. + * Current implementation returns the number of AT commands handled since + * boot. This is a good indicator for spammy headset/handsfree units that + * can keep the device awake by polling for cellular status updates. As a + * rule of thumb, each AT command prevents the CPU from sleeping for 500 ms + * @return monotonically increasing battery usage hint, or a negative error + * code on error + * @hide + */ + public int getBatteryUsageHint() { + if (DBG) log("getBatteryUsageHint()"); + if (mService != null) { + try { + return mService.getBatteryUsageHint(); + } catch (RemoteException e) {Log.e(TAG, e.toString());} + } else { + Log.w(TAG, "Proxy not attached to service"); + if (DBG) Log.d(TAG, Log.getStackTraceString(new Throwable())); + } + return -1; + } + + /** * Check class bits for possible HSP or HFP support. * This is a simple heuristic that tries to guess if a device with the * given class bits might support HSP or HFP. It is not accurate for all diff --git a/core/java/android/bluetooth/BluetoothInputStream.java b/core/java/android/bluetooth/BluetoothInputStream.java index e6f501c..c060f32 100644 --- a/core/java/android/bluetooth/BluetoothInputStream.java +++ b/core/java/android/bluetooth/BluetoothInputStream.java @@ -59,7 +59,7 @@ import java.io.InputStream; byte b[] = new byte[1]; int ret = mSocket.readNative(b, 0, 1); if (ret == 1) { - return (int)b[0]; + return (int)b[0] & 0xff; } else { return -1; } diff --git a/core/java/android/bluetooth/HeadsetBase.java b/core/java/android/bluetooth/HeadsetBase.java index f31e7a2..f987ffd 100644 --- a/core/java/android/bluetooth/HeadsetBase.java +++ b/core/java/android/bluetooth/HeadsetBase.java @@ -40,6 +40,8 @@ public class HeadsetBase { public static final int DIRECTION_INCOMING = 1; public static final int DIRECTION_OUTGOING = 2; + private static int sAtInputCount = 0; /* TODO: Consider not using a static variable */ + private final BluetoothDevice mBluetooth; private final String mAddress; private final int mRfcommChannel; @@ -109,6 +111,14 @@ public class HeadsetBase { acquireWakeLock(); long timestamp; + synchronized(HeadsetBase.class) { + if (sAtInputCount == Integer.MAX_VALUE) { + sAtInputCount = 0; + } else { + sAtInputCount++; + } + } + if (DBG) timestamp = System.currentTimeMillis(); AtCommandResult result = mAtParser.process(input); if (DBG) Log.d(TAG, "Processing " + input + " took " + @@ -279,7 +289,11 @@ public class HeadsetBase { } } - private void log(String msg) { + public static int getAtInputCount() { + return sAtInputCount; + } + + private static void log(String msg) { Log.d(TAG, msg); } } diff --git a/core/java/android/bluetooth/IBluetoothHeadset.aidl b/core/java/android/bluetooth/IBluetoothHeadset.aidl index 582d4e3..5f42fd6 100644 --- a/core/java/android/bluetooth/IBluetoothHeadset.aidl +++ b/core/java/android/bluetooth/IBluetoothHeadset.aidl @@ -31,4 +31,5 @@ interface IBluetoothHeadset { boolean stopVoiceRecognition(); boolean setPriority(in String address, int priority); int getPriority(in String address); + int getBatteryUsageHint(); } diff --git a/core/java/android/content/AbstractThreadedSyncAdapter.java b/core/java/android/content/AbstractThreadedSyncAdapter.java new file mode 100644 index 0000000..f15a902 --- /dev/null +++ b/core/java/android/content/AbstractThreadedSyncAdapter.java @@ -0,0 +1,176 @@ +/* + * 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.content; + +import android.accounts.Account; +import android.os.Bundle; +import android.os.Process; + +import java.util.concurrent.atomic.AtomicInteger; + +/** + * An abstract implementation of a SyncAdapter that spawns a thread to invoke a sync operation. + * If a sync operation is already in progress when a startSync() request is received then an error + * will be returned to the new request and the existing request will be allowed to continue. + * When a startSync() is received and there is no sync operation in progress then a thread + * will be started to run the operation and {@link #performSync} will be invoked on that thread. + * If a cancelSync() is received that matches an existing sync operation then the thread + * that is running that sync operation will be interrupted, which will indicate to the thread + * that the sync has been canceled. + * + * @hide + */ +public abstract class AbstractThreadedSyncAdapter { + private final Context mContext; + private final AtomicInteger mNumSyncStarts; + private final ISyncAdapterImpl mISyncAdapterImpl; + + // all accesses to this member variable must be synchronized on "this" + private SyncThread mSyncThread; + + /** Kernel event log tag. Also listed in data/etc/event-log-tags. */ + public static final int LOG_SYNC_DETAILS = 2743; + + /** + * Creates an {@link AbstractThreadedSyncAdapter}. + * @param context the {@link Context} that this is running within. + */ + public AbstractThreadedSyncAdapter(Context context) { + mContext = context; + mISyncAdapterImpl = new ISyncAdapterImpl(); + mNumSyncStarts = new AtomicInteger(0); + mSyncThread = null; + } + + class ISyncAdapterImpl extends ISyncAdapter.Stub { + public void startSync(ISyncContext syncContext, String authority, Account account, + Bundle extras) { + final SyncContext syncContextClient = new SyncContext(syncContext); + + boolean alreadyInProgress; + // synchronize to make sure that mSyncThread doesn't change between when we + // check it and when we use it + synchronized (this) { + if (mSyncThread == null) { + mSyncThread = new SyncThread( + "SyncAdapterThread-" + mNumSyncStarts.incrementAndGet(), + syncContextClient, authority, account, extras); + Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); + mSyncThread.start(); + alreadyInProgress = false; + } else { + alreadyInProgress = true; + } + } + + // do this outside since we don't want to call back into the syncContext while + // holding the synchronization lock + if (alreadyInProgress) { + syncContextClient.onFinished(SyncResult.ALREADY_IN_PROGRESS); + } + } + + public void cancelSync(ISyncContext syncContext) { + // synchronize to make sure that mSyncThread doesn't change between when we + // check it and when we use it + synchronized (this) { + if (mSyncThread != null + && mSyncThread.mSyncContext.getISyncContext() == syncContext) { + mSyncThread.interrupt(); + } + } + } + } + + /** + * The thread that invokes performSync(). It also acquires the provider for this sync + * before calling performSync and releases it afterwards. Cancel this thread in order to + * cancel the sync. + */ + private class SyncThread extends Thread { + private final SyncContext mSyncContext; + private final String mAuthority; + private final Account mAccount; + private final Bundle mExtras; + + private SyncThread(String name, SyncContext syncContext, String authority, + Account account, Bundle extras) { + super(name); + mSyncContext = syncContext; + mAuthority = authority; + mAccount = account; + mExtras = extras; + } + + public void run() { + if (isCanceled()) { + return; + } + + SyncResult syncResult = new SyncResult(); + ContentProviderClient provider = null; + try { + provider = mContext.getContentResolver().acquireContentProviderClient(mAuthority); + if (provider != null) { + AbstractThreadedSyncAdapter.this.performSync(mAccount, mExtras, + mAuthority, provider, syncResult); + } else { + // TODO(fredq) update the syncResults to indicate that we were unable to + // find the provider. maybe with a ProviderError? + } + } finally { + if (provider != null) { + provider.release(); + } + if (!isCanceled()) { + mSyncContext.onFinished(syncResult); + } + // synchronize so that the assignment will be seen by other threads + // that also synchronize accesses to mSyncThread + synchronized (this) { + mSyncThread = null; + } + } + } + + private boolean isCanceled() { + return Thread.currentThread().isInterrupted(); + } + } + + /** + * @return a reference to the ISyncAdapter interface into this SyncAdapter implementation. + */ + public final ISyncAdapter getISyncAdapter() { + return mISyncAdapterImpl; + } + + /** + * Perform a sync for this account. SyncAdapter-specific parameters may + * be specified in extras, which is guaranteed to not be null. Invocations + * of this method are guaranteed to be serialized. + * + * @param account the account that should be synced + * @param extras SyncAdapter-specific parameters + * @param authority the authority of this sync request + * @param provider a ContentProviderClient that points to the ContentProvider for this + * authority + * @param syncResult SyncAdapter-specific parameters + */ + public abstract void performSync(Account account, Bundle extras, + String authority, ContentProviderClient provider, SyncResult syncResult); +}
\ No newline at end of file diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 4ccbf18..6c8bafc 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -16,6 +16,7 @@ package android.content; +import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.res.AssetManager; import android.content.res.Resources; @@ -233,6 +234,9 @@ public abstract class Context { /** Return the name of this application's package. */ public abstract String getPackageName(); + /** Return the full application info for this context's package. */ + public abstract ApplicationInfo getApplicationInfo(); + /** * {@hide} * Return the full path to this context's resource files. This is the ZIP files diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java index 262204e..7513b3b 100644 --- a/core/java/android/content/ContextWrapper.java +++ b/core/java/android/content/ContextWrapper.java @@ -16,6 +16,7 @@ package android.content; +import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.res.AssetManager; import android.content.res.Resources; @@ -120,6 +121,11 @@ public class ContextWrapper extends Context { } @Override + public ApplicationInfo getApplicationInfo() { + return mBase.getApplicationInfo(); + } + + @Override public String getPackageResourcePath() { return mBase.getPackageResourcePath(); } diff --git a/core/java/android/content/IIntentReceiver.aidl b/core/java/android/content/IIntentReceiver.aidl new file mode 100755 index 0000000..443db2d --- /dev/null +++ b/core/java/android/content/IIntentReceiver.aidl @@ -0,0 +1,33 @@ +/* + * 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 android.content; + +import android.content.Intent; +import android.os.Bundle; + +/** + * System private API for dispatching intent broadcasts. This is given to the + * activity manager as part of registering for an intent broadcasts, and is + * called when it receives intents. + * + * {@hide} + */ +oneway interface IIntentReceiver { + void performReceive(in Intent intent, int resultCode, + String data, in Bundle extras, boolean ordered); +} + diff --git a/core/java/android/backup/RestoreHelperDistributor.java b/core/java/android/content/IIntentSender.aidl index 555ca79..b7da472 100644 --- a/core/java/android/backup/RestoreHelperDistributor.java +++ b/core/java/android/content/IIntentSender.aidl @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009 The Android Open Source Project + * 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. @@ -14,15 +14,13 @@ * limitations under the License. */ -package android.backup; +package android.content; -import java.util.HashMap; +import android.content.IIntentReceiver; +import android.content.Intent; /** @hide */ -public class RestoreHelperDistributor { - HashMap<String,RestoreHelper> mHelpers; - - public void addHelper(String keyPrefix, RestoreHelper helper) { - mHelpers.put(keyPrefix, helper); - } +interface IIntentSender { + int send(int code, in Intent intent, String resolvedType, + IIntentReceiver finishedReceiver); } diff --git a/core/java/android/content/ISyncAdapter.aidl b/core/java/android/content/ISyncAdapter.aidl index d228605..4660527 100644 --- a/core/java/android/content/ISyncAdapter.aidl +++ b/core/java/android/content/ISyncAdapter.aidl @@ -31,14 +31,17 @@ oneway interface ISyncAdapter { * * @param syncContext the ISyncContext used to indicate the progress of the sync. When * the sync is finished (successfully or not) ISyncContext.onFinished() must be called. + * @param authority the authority that should be synced * @param account the account that should be synced * @param extras SyncAdapter-specific parameters */ - void startSync(ISyncContext syncContext, in Account account, in Bundle extras); + void startSync(ISyncContext syncContext, String authority, + in Account account, in Bundle extras); /** * Cancel the most recently initiated sync. Due to race conditions, this may arrive * after the ISyncContext.onFinished() for that sync was called. + * @param syncContext the ISyncContext that was passed to {@link #startSync} */ - void cancelSync(); + void cancelSync(ISyncContext syncContext); } diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 0e6be0a..33e769e 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -240,35 +240,35 @@ import java.util.Set; * * <activity class=".NotesList" android:label="@string/title_notes_list"> * <intent-filter> - * <action android:value="android.intent.action.MAIN" /> - * <category android:value="android.intent.category.LAUNCHER" /> + * <action android:name="android.intent.action.MAIN" /> + * <category android:name="android.intent.category.LAUNCHER" /> * </intent-filter> * <intent-filter> - * <action android:value="android.intent.action.VIEW" /> - * <action android:value="android.intent.action.EDIT" /> - * <action android:value="android.intent.action.PICK" /> - * <category android:value="android.intent.category.DEFAULT" /> - * <type android:value="vnd.android.cursor.dir/<i>vnd.google.note</i>" /> + * <action android:name="android.intent.action.VIEW" /> + * <action android:name="android.intent.action.EDIT" /> + * <action android:name="android.intent.action.PICK" /> + * <category android:name="android.intent.category.DEFAULT" /> + * <data android:mimeType="vnd.android.cursor.dir/<i>vnd.google.note</i>" /> * </intent-filter> * <intent-filter> - * <action android:value="android.intent.action.GET_CONTENT" /> - * <category android:value="android.intent.category.DEFAULT" /> - * <type android:value="vnd.android.cursor.item/<i>vnd.google.note</i>" /> + * <action android:name="android.intent.action.GET_CONTENT" /> + * <category android:name="android.intent.category.DEFAULT" /> + * <data android:mimeType="vnd.android.cursor.item/<i>vnd.google.note</i>" /> * </intent-filter> * </activity> * * <activity class=".NoteEditor" android:label="@string/title_note"> * <intent-filter android:label="@string/resolve_edit"> - * <action android:value="android.intent.action.VIEW" /> - * <action android:value="android.intent.action.EDIT" /> - * <category android:value="android.intent.category.DEFAULT" /> - * <type android:value="vnd.android.cursor.item/<i>vnd.google.note</i>" /> + * <action android:name="android.intent.action.VIEW" /> + * <action android:name="android.intent.action.EDIT" /> + * <category android:name="android.intent.category.DEFAULT" /> + * <data android:mimeType="vnd.android.cursor.item/<i>vnd.google.note</i>" /> * </intent-filter> * * <intent-filter> - * <action android:value="android.intent.action.INSERT" /> - * <category android:value="android.intent.category.DEFAULT" /> - * <type android:value="vnd.android.cursor.dir/<i>vnd.google.note</i>" /> + * <action android:name="android.intent.action.INSERT" /> + * <category android:name="android.intent.category.DEFAULT" /> + * <data android:mimeType="vnd.android.cursor.dir/<i>vnd.google.note</i>" /> * </intent-filter> * * </activity> @@ -276,11 +276,11 @@ import java.util.Set; * <activity class=".TitleEditor" android:label="@string/title_edit_title" * android:theme="@android:style/Theme.Dialog"> * <intent-filter android:label="@string/resolve_title"> - * <action android:value="<i>com.android.notepad.action.EDIT_TITLE</i>" /> - * <category android:value="android.intent.category.DEFAULT" /> - * <category android:value="android.intent.category.ALTERNATIVE" /> - * <category android:value="android.intent.category.SELECTED_ALTERNATIVE" /> - * <type android:value="vnd.android.cursor.item/<i>vnd.google.note</i>" /> + * <action android:name="<i>com.android.notepad.action.EDIT_TITLE</i>" /> + * <category android:name="android.intent.category.DEFAULT" /> + * <category android:name="android.intent.category.ALTERNATIVE" /> + * <category android:name="android.intent.category.SELECTED_ALTERNATIVE" /> + * <data android:mimeType="vnd.android.cursor.item/<i>vnd.google.note</i>" /> * </intent-filter> * </activity> * @@ -294,8 +294,8 @@ import java.util.Set; * <ol> * <li><pre> * <intent-filter> - * <action android:value="{@link #ACTION_MAIN android.intent.action.MAIN}" /> - * <category android:value="{@link #CATEGORY_LAUNCHER android.intent.category.LAUNCHER}" /> + * <action android:name="{@link #ACTION_MAIN android.intent.action.MAIN}" /> + * <category android:name="{@link #CATEGORY_LAUNCHER android.intent.category.LAUNCHER}" /> * </intent-filter></pre> * <p>This provides a top-level entry into the NotePad application: the standard * MAIN action is a main entry point (not requiring any other information in @@ -303,11 +303,11 @@ import java.util.Set; * listed in the application launcher.</p> * <li><pre> * <intent-filter> - * <action android:value="{@link #ACTION_VIEW android.intent.action.VIEW}" /> - * <action android:value="{@link #ACTION_EDIT android.intent.action.EDIT}" /> - * <action android:value="{@link #ACTION_PICK android.intent.action.PICK}" /> - * <category android:value="{@link #CATEGORY_DEFAULT android.intent.category.DEFAULT}" /> - * <type android:value="vnd.android.cursor.dir/<i>vnd.google.note</i>" /> + * <action android:name="{@link #ACTION_VIEW android.intent.action.VIEW}" /> + * <action android:name="{@link #ACTION_EDIT android.intent.action.EDIT}" /> + * <action android:name="{@link #ACTION_PICK android.intent.action.PICK}" /> + * <category android:name="{@link #CATEGORY_DEFAULT android.intent.category.DEFAULT}" /> + * <data mimeType:name="vnd.android.cursor.dir/<i>vnd.google.note</i>" /> * </intent-filter></pre> * <p>This declares the things that the activity can do on a directory of * notes. The type being supported is given with the <type> tag, where @@ -322,9 +322,9 @@ import java.util.Set; * activity when its component name is not explicitly specified.</p> * <li><pre> * <intent-filter> - * <action android:value="{@link #ACTION_GET_CONTENT android.intent.action.GET_CONTENT}" /> - * <category android:value="{@link #CATEGORY_DEFAULT android.intent.category.DEFAULT}" /> - * <type android:value="vnd.android.cursor.item/<i>vnd.google.note</i>" /> + * <action android:name="{@link #ACTION_GET_CONTENT android.intent.action.GET_CONTENT}" /> + * <category android:name="{@link #CATEGORY_DEFAULT android.intent.category.DEFAULT}" /> + * <data android:mimeType="vnd.android.cursor.item/<i>vnd.google.note</i>" /> * </intent-filter></pre> * <p>This filter describes the ability return to the caller a note selected by * the user without needing to know where it came from. The data type @@ -371,10 +371,10 @@ import java.util.Set; * <ol> * <li><pre> * <intent-filter android:label="@string/resolve_edit"> - * <action android:value="{@link #ACTION_VIEW android.intent.action.VIEW}" /> - * <action android:value="{@link #ACTION_EDIT android.intent.action.EDIT}" /> - * <category android:value="{@link #CATEGORY_DEFAULT android.intent.category.DEFAULT}" /> - * <type android:value="vnd.android.cursor.item/<i>vnd.google.note</i>" /> + * <action android:name="{@link #ACTION_VIEW android.intent.action.VIEW}" /> + * <action android:name="{@link #ACTION_EDIT android.intent.action.EDIT}" /> + * <category android:name="{@link #CATEGORY_DEFAULT android.intent.category.DEFAULT}" /> + * <data android:mimeType="vnd.android.cursor.item/<i>vnd.google.note</i>" /> * </intent-filter></pre> * <p>The first, primary, purpose of this activity is to let the user interact * with a single note, as decribed by the MIME type @@ -384,9 +384,9 @@ import java.util.Set; * specifying its component.</p> * <li><pre> * <intent-filter> - * <action android:value="{@link #ACTION_INSERT android.intent.action.INSERT}" /> - * <category android:value="{@link #CATEGORY_DEFAULT android.intent.category.DEFAULT}" /> - * <type android:value="vnd.android.cursor.dir/<i>vnd.google.note</i>" /> + * <action android:name="{@link #ACTION_INSERT android.intent.action.INSERT}" /> + * <category android:name="{@link #CATEGORY_DEFAULT android.intent.category.DEFAULT}" /> + * <data android:mimeType="vnd.android.cursor.dir/<i>vnd.google.note</i>" /> * </intent-filter></pre> * <p>The secondary use of this activity is to insert a new note entry into * an existing directory of notes. This is used when the user creates a new @@ -422,11 +422,11 @@ import java.util.Set; * * <pre> * <intent-filter android:label="@string/resolve_title"> - * <action android:value="<i>com.android.notepad.action.EDIT_TITLE</i>" /> - * <category android:value="{@link #CATEGORY_DEFAULT android.intent.category.DEFAULT}" /> - * <category android:value="{@link #CATEGORY_ALTERNATIVE android.intent.category.ALTERNATIVE}" /> - * <category android:value="{@link #CATEGORY_SELECTED_ALTERNATIVE android.intent.category.SELECTED_ALTERNATIVE}" /> - * <type android:value="vnd.android.cursor.item/<i>vnd.google.note</i>" /> + * <action android:name="<i>com.android.notepad.action.EDIT_TITLE</i>" /> + * <category android:name="{@link #CATEGORY_DEFAULT android.intent.category.DEFAULT}" /> + * <category android:name="{@link #CATEGORY_ALTERNATIVE android.intent.category.ALTERNATIVE}" /> + * <category android:name="{@link #CATEGORY_SELECTED_ALTERNATIVE android.intent.category.SELECTED_ALTERNATIVE}" /> + * <data android:mimeType="vnd.android.cursor.item/<i>vnd.google.note</i>" /> * </intent-filter></pre> * * <p>In the single intent template here, we @@ -509,8 +509,8 @@ import java.util.Set; * <li> {@link #ACTION_UID_REMOVED} * <li> {@link #ACTION_BATTERY_CHANGED} * <li> {@link #ACTION_POWER_CONNECTED} - * <li> {@link #ACTION_POWER_DISCONNECTED} - * <li> {@link #ACTION_SHUTDOWN} + * <li> {@link #ACTION_POWER_DISCONNECTED} + * <li> {@link #ACTION_SHUTDOWN} * </ul> * * <h3>Standard Categories</h3> @@ -915,6 +915,23 @@ public class Intent implements Parcelable { @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String ACTION_SEND = "android.intent.action.SEND"; /** + * Activity Action: Deliver multiple data to someone else. + * <p> + * Like ACTION_SEND, except the data is multiple. + * <p> + * Input: {@link #getType} is the MIME type of the data being sent. + * get*ArrayListExtra can have either a {@link #EXTRA_TEXT} or {@link + * #EXTRA_STREAM} field, containing the data to be sent. + * <p> + * Optional standard extras, which may be interpreted by some recipients as + * appropriate, are: {@link #EXTRA_EMAIL}, {@link #EXTRA_CC}, + * {@link #EXTRA_BCC}, {@link #EXTRA_SUBJECT}. + * <p> + * Output: nothing. + */ + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_SEND_MULTIPLE = "android.intent.action.SEND_MULTIPLE"; + /** * Activity Action: Handle an incoming phone call. * <p>Input: nothing. * <p>Output: nothing. @@ -1059,6 +1076,15 @@ public class Intent implements Parcelable { */ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String ACTION_APP_ERROR = "android.intent.action.APP_ERROR"; + + /** + * Activity Action: Show power usage information to the user. + * <p>Input: Nothing. + * <p>Output: Nothing. + */ + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_POWER_USAGE_SUMMARY = "android.intent.action.POWER_USAGE_SUMMARY"; + // --------------------------------------------------------------------- // --------------------------------------------------------------------- // Standard intent broadcast actions (see action variable). @@ -1277,11 +1303,11 @@ public class Intent implements Parcelable { * This is intended for applications that wish to register specifically to this notification. * Unlike ACTION_BATTERY_CHANGED, applications will be woken for this and so do not have to * stay active to receive this notification. This action can be used to implement actions - * that wait until power is available to trigger. + * that wait until power is available to trigger. */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) public static final String ACTION_POWER_DISCONNECTED = - "android.intent.action.POWER_DISCONNECTED"; + "android.intent.action.POWER_DISCONNECTED"; /** * Broadcast Action: Device is shutting down. * This is broadcast when the device is being shut down (completely turned @@ -1290,7 +1316,7 @@ public class Intent implements Parcelable { * to handle this, since the forground activity will be paused as well. */ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) - public static final String ACTION_SHUTDOWN = "android.intent.action.ACTION_SHUTDOWN"; + public static final String ACTION_SHUTDOWN = "android.intent.action.ACTION_SHUTDOWN"; /** * Broadcast Action: Indicates low memory condition on the device */ @@ -1807,23 +1833,23 @@ public class Intent implements Parcelable { * delivered. */ public static final String EXTRA_ALARM_COUNT = "android.intent.extra.ALARM_COUNT"; - + /** * Used as a parcelable extra field in {@link #ACTION_APP_ERROR}, containing * the bug report. - * + * * @hide */ public static final String EXTRA_BUG_REPORT = "android.intent.extra.BUG_REPORT"; /** - * Used as a string extra field when sending an intent to PackageInstaller to install a + * Used as a string extra field when sending an intent to PackageInstaller to install a * package. Specifies the installer package name; this package will receive the * {@link #ACTION_APP_ERROR} intent. - * + * * @hide */ - public static final String EXTRA_INSTALLER_PACKAGE_NAME + public static final String EXTRA_INSTALLER_PACKAGE_NAME = "android.intent.extra.INSTALLER_PACKAGE_NAME"; /** @@ -2063,10 +2089,25 @@ public class Intent implements Parcelable { public static final int FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT = 0x20000000; // --------------------------------------------------------------------- + // --------------------------------------------------------------------- + // toUri() and parseUri() options. + + /** + * Flag for use with {@link #toUri} and {@link #parseUri}: the URI string + * always has the "intent:" scheme. This syntax can be used when you want + * to later disambiguate between URIs that are intended to describe an + * Intent vs. all others that should be treated as raw URIs. When used + * with {@link #parseUri}, any other scheme will result in a generic + * VIEW action for that raw URI. + */ + public static final int URI_INTENT_SCHEME = 1<<0; + + // --------------------------------------------------------------------- private String mAction; private Uri mData; private String mType; + private String mPackage; private ComponentName mComponent; private int mFlags; private HashSet<String> mCategories; @@ -2087,6 +2128,7 @@ public class Intent implements Parcelable { this.mAction = o.mAction; this.mData = o.mData; this.mType = o.mType; + this.mPackage = o.mPackage; this.mComponent = o.mComponent; this.mFlags = o.mFlags; if (o.mCategories != null) { @@ -2106,6 +2148,7 @@ public class Intent implements Parcelable { this.mAction = o.mAction; this.mData = o.mData; this.mType = o.mType; + this.mPackage = o.mPackage; this.mComponent = o.mComponent; if (o.mCategories != null) { this.mCategories = new HashSet<String>(o.mCategories); @@ -2206,23 +2249,50 @@ public class Intent implements Parcelable { } /** + * Call {@link #parseUri} with 0 flags. + * @deprecated Use {@link #parseUri} instead. + */ + @Deprecated + public static Intent getIntent(String uri) throws URISyntaxException { + return parseUri(uri, 0); + } + + /** * Create an intent from a URI. This URI may encode the action, - * category, and other intent fields, if it was returned by toURI(). If - * the Intent was not generate by toURI(), its data will be the entire URI - * and its action will be ACTION_VIEW. + * category, and other intent fields, if it was returned by + * {@link #toUri}.. If the Intent was not generate by toUri(), its data + * will be the entire URI and its action will be ACTION_VIEW. * * <p>The URI given here must not be relative -- that is, it must include * the scheme and full path. * * @param uri The URI to turn into an Intent. + * @param flags Additional processing flags. Either 0 or * * @return Intent The newly created Intent object. * - * @see #toURI + * @throws URISyntaxException Throws URISyntaxError if the basic URI syntax + * it bad (as parsed by the Uri class) or the Intent data within the + * URI is invalid. + * + * @see #toUri */ - public static Intent getIntent(String uri) throws URISyntaxException { + public static Intent parseUri(String uri, int flags) throws URISyntaxException { int i = 0; try { + // Validate intent scheme for if requested. + if ((flags&URI_INTENT_SCHEME) != 0) { + if (!uri.startsWith("intent:")) { + Intent intent = new Intent(ACTION_VIEW); + try { + intent.setData(Uri.parse(uri)); + } catch (IllegalArgumentException e) { + throw new URISyntaxException(uri, e.getMessage()); + } + return intent; + } + } + // simple case i = uri.lastIndexOf("#"); if (i == -1) return new Intent(ACTION_VIEW, Uri.parse(uri)); @@ -2234,16 +2304,15 @@ public class Intent implements Parcelable { Intent intent = new Intent(ACTION_VIEW); // fetch data part, if present - if (i > 0) { - intent.mData = Uri.parse(uri.substring(0, i)); - } + String data = i >= 0 ? uri.substring(0, i) : null; + String scheme = null; i += "#Intent;".length(); // loop over contents of Intent, all name=value; while (!uri.startsWith("end", i)) { int eq = uri.indexOf('=', i); int semi = uri.indexOf(';', eq); - String value = uri.substring(eq + 1, semi); + String value = Uri.decode(uri.substring(eq + 1, semi)); // action if (uri.startsWith("action=", i)) { @@ -2265,15 +2334,24 @@ public class Intent implements Parcelable { intent.mFlags = Integer.decode(value).intValue(); } + // package + else if (uri.startsWith("package=", i)) { + intent.mPackage = value; + } + // component else if (uri.startsWith("component=", i)) { intent.mComponent = ComponentName.unflattenFromString(value); } + // scheme + else if (uri.startsWith("scheme=", i)) { + scheme = value; + } + // extra else { String key = Uri.decode(uri.substring(i + 2, eq)); - value = Uri.decode(value); // create Bundle if it doesn't already exist if (intent.mExtras == null) intent.mExtras = new Bundle(); Bundle b = intent.mExtras; @@ -2294,6 +2372,23 @@ public class Intent implements Parcelable { i = semi + 1; } + if (data != null) { + if (data.startsWith("intent:")) { + data = data.substring(7); + if (scheme != null) { + data = scheme + ':' + data; + } + } + + if (data.length() > 0) { + try { + intent.mData = Uri.parse(data); + } catch (IllegalArgumentException e) { + throw new URISyntaxException(uri, e.getMessage()); + } + } + } + return intent; } catch (IndexOutOfBoundsException e) { @@ -3107,6 +3202,20 @@ public class Intent implements Parcelable { } /** + * Retrieve the application package name this Intent is limited to. When + * resolving an Intent, if non-null this limits the resolution to only + * components in the given application package. + * + * @return The name of the application package for the Intent. + * + * @see #resolveActivity + * @see #setPackage + */ + public String getPackage() { + return mPackage; + } + + /** * Retrieve the concrete component associated with the intent. When receiving * an intent, this is the component that was found to best handle it (that is, * yourself) and will always be non-null; in all other cases it will be @@ -3141,6 +3250,9 @@ public class Intent implements Parcelable { * <p>If {@link #addCategory} has added any categories, the activity must * handle ALL of the categories specified. * + * <p>If {@link #getPackage} is non-NULL, only activity components in + * that application package will be considered. + * * <p>If there are no activities that satisfy all of these conditions, a * null string is returned. * @@ -3262,7 +3374,7 @@ public class Intent implements Parcelable { * only specify a type and not data, for example to indicate the type of * data to return. This method automatically clears any data that was * previously set by {@link #setData}. - * + * * <p><em>Note: MIME type matching in the Android framework is * case-sensitive, unlike formal RFC MIME types. As a result, * you should always write your MIME types with lower case letters, @@ -4112,6 +4224,27 @@ public class Intent implements Parcelable { } /** + * (Usually optional) Set an explicit application package name that limits + * the components this Intent will resolve to. If left to the default + * value of null, all components in all applications will considered. + * If non-null, the Intent can only match the components in the given + * application package. + * + * @param packageName The name of the application package to handle the + * intent, or null to allow any application package. + * + * @return Returns the same Intent object, for chaining multiple calls + * into a single statement. + * + * @see #getPackage + * @see #resolveActivity + */ + public Intent setPackage(String packageName) { + mPackage = packageName; + return this; + } + + /** * (Usually optional) Explicitly set the component to handle the intent. * If left with the default value of null, the system will determine the * appropriate class to use based on the other fields (action, data, @@ -4223,6 +4356,12 @@ public class Intent implements Parcelable { public static final int FILL_IN_COMPONENT = 1<<3; /** + * Use with {@link #fillIn} to allow the current package value to be + * overwritten, even if it is already set. + */ + public static final int FILL_IN_PACKAGE = 1<<4; + + /** * Copy the contents of <var>other</var> in to this object, but only * where fields are not defined by this object. For purposes of a field * being defined, the following pieces of data in the Intent are @@ -4233,14 +4372,15 @@ public class Intent implements Parcelable { * <li> data URI and MIME type, as set by {@link #setData(Uri)}, * {@link #setType(String)}, or {@link #setDataAndType(Uri, String)}. * <li> categories, as set by {@link #addCategory}. + * <li> package, as set by {@link #setPackage}. * <li> component, as set by {@link #setComponent(ComponentName)} or * related methods. * <li> each top-level name in the associated extras. * </ul> * * <p>In addition, you can use the {@link #FILL_IN_ACTION}, - * {@link #FILL_IN_DATA}, {@link #FILL_IN_CATEGORIES}, and - * {@link #FILL_IN_COMPONENT} to override the restriction where the + * {@link #FILL_IN_DATA}, {@link #FILL_IN_CATEGORIES}, {@link #FILL_IN_PACKAGE}, + * and {@link #FILL_IN_COMPONENT} to override the restriction where the * corresponding field will not be replaced if it is already set. * * <p>For example, consider Intent A with {data="foo", categories="bar"} @@ -4256,32 +4396,39 @@ public class Intent implements Parcelable { * @param flags Options to control which fields can be filled in. * * @return Returns a bit mask of {@link #FILL_IN_ACTION}, - * {@link #FILL_IN_DATA}, {@link #FILL_IN_CATEGORIES}, and - * {@link #FILL_IN_COMPONENT} indicating which fields were changed. + * {@link #FILL_IN_DATA}, {@link #FILL_IN_CATEGORIES}, {@link #FILL_IN_PACKAGE}, + * and {@link #FILL_IN_COMPONENT} indicating which fields were changed. */ public int fillIn(Intent other, int flags) { int changes = 0; - if ((mAction == null && other.mAction == null) - || (flags&FILL_IN_ACTION) != 0) { + if (other.mAction != null + && (mAction == null || (flags&FILL_IN_ACTION) != 0)) { mAction = other.mAction; changes |= FILL_IN_ACTION; } - if ((mData == null && mType == null && - (other.mData != null || other.mType != null)) - || (flags&FILL_IN_DATA) != 0) { + if ((other.mData != null || other.mType != null) + && ((mData == null && mType == null) + || (flags&FILL_IN_DATA) != 0)) { mData = other.mData; mType = other.mType; changes |= FILL_IN_DATA; } - if ((mCategories == null && other.mCategories == null) - || (flags&FILL_IN_CATEGORIES) != 0) { + if (other.mCategories != null + && (mCategories == null || (flags&FILL_IN_CATEGORIES) != 0)) { if (other.mCategories != null) { mCategories = new HashSet<String>(other.mCategories); } changes |= FILL_IN_CATEGORIES; } - if ((mComponent == null && other.mComponent == null) - || (flags&FILL_IN_COMPONENT) != 0) { + if (other.mPackage != null + && (mPackage == null || (flags&FILL_IN_PACKAGE) != 0)) { + mPackage = other.mPackage; + changes |= FILL_IN_PACKAGE; + } + // Component is special: it can -only- be set if explicitly allowed, + // since otherwise the sender could force the intent somewhere the + // originator didn't intend. + if (other.mComponent != null && (flags&FILL_IN_COMPONENT) != 0) { mComponent = other.mComponent; changes |= FILL_IN_COMPONENT; } @@ -4396,6 +4543,17 @@ public class Intent implements Parcelable { } } } + if (mPackage != other.mPackage) { + if (mPackage != null) { + if (!mPackage.equals(other.mPackage)) { + return false; + } + } else { + if (!other.mPackage.equals(mPackage)) { + return false; + } + } + } if (mComponent != other.mComponent) { if (mComponent != null) { if (!mComponent.equals(other.mComponent)) { @@ -4441,6 +4599,9 @@ public class Intent implements Parcelable { if (mType != null) { code += mType.hashCode(); } + if (mPackage != null) { + code += mPackage.hashCode(); + } if (mComponent != null) { code += mComponent.hashCode(); } @@ -4467,7 +4628,7 @@ public class Intent implements Parcelable { toShortString(b, comp, extras); return b.toString(); } - + /** @hide */ public void toShortString(StringBuilder b, boolean comp, boolean extras) { boolean first = true; @@ -4511,6 +4672,13 @@ public class Intent implements Parcelable { first = false; b.append("flg=0x").append(Integer.toHexString(mFlags)); } + if (mPackage != null) { + if (!first) { + b.append(' '); + } + first = false; + b.append("pkg=").append(mPackage); + } if (comp && mComponent != null) { if (!first) { b.append(' '); @@ -4527,28 +4695,87 @@ public class Intent implements Parcelable { } } + /** + * Call {@link #toUri} with 0 flags. + * @deprecated Use {@link #toUri} instead. + */ + @Deprecated public String toURI() { + return toUri(0); + } + + /** + * Convert this Intent into a String holding a URI representation of it. + * The returned URI string has been properly URI encoded, so it can be + * used with {@link Uri#parse Uri.parse(String)}. The URI contains the + * Intent's data as the base URI, with an additional fragment describing + * the action, categories, type, flags, package, component, and extras. + * + * <p>You can convert the returned string back to an Intent with + * {@link #getIntent}. + * + * @param flags Additional operating flags. Either 0 or + * {@link #URI_INTENT_SCHEME}. + * + * @return Returns a URI encoding URI string describing the entire contents + * of the Intent. + */ + public String toUri(int flags) { StringBuilder uri = new StringBuilder(128); - if (mData != null) uri.append(mData.toString()); + String scheme = null; + if (mData != null) { + String data = mData.toString(); + if ((flags&URI_INTENT_SCHEME) != 0) { + final int N = data.length(); + for (int i=0; i<N; i++) { + char c = data.charAt(i); + if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') + || c == '.' || c == '-') { + continue; + } + if (c == ':' && i > 0) { + // Valid scheme. + scheme = data.substring(0, i); + uri.append("intent:"); + data = data.substring(i+1); + break; + } + + // No scheme. + break; + } + } + uri.append(data); + + } else if ((flags&URI_INTENT_SCHEME) != 0) { + uri.append("intent:"); + } uri.append("#Intent;"); + if (scheme != null) { + uri.append("scheme=").append(scheme).append(';'); + } if (mAction != null) { - uri.append("action=").append(mAction).append(';'); + uri.append("action=").append(Uri.encode(mAction)).append(';'); } if (mCategories != null) { for (String category : mCategories) { - uri.append("category=").append(category).append(';'); + uri.append("category=").append(Uri.encode(category)).append(';'); } } if (mType != null) { - uri.append("type=").append(mType).append(';'); + uri.append("type=").append(Uri.encode(mType, "/")).append(';'); } if (mFlags != 0) { uri.append("launchFlags=0x").append(Integer.toHexString(mFlags)).append(';'); } + if (mPackage != null) { + uri.append("package=").append(Uri.encode(mPackage)).append(';'); + } if (mComponent != null) { - uri.append("component=").append(mComponent.flattenToShortString()).append(';'); + uri.append("component=").append(Uri.encode( + mComponent.flattenToShortString(), "/")).append(';'); } if (mExtras != null) { for (String key : mExtras.keySet()) { @@ -4590,6 +4817,7 @@ public class Intent implements Parcelable { Uri.writeToParcel(out, mData); out.writeString(mType); out.writeInt(mFlags); + out.writeString(mPackage); ComponentName.writeToParcel(mComponent, out); if (mCategories != null) { @@ -4623,6 +4851,7 @@ public class Intent implements Parcelable { mData = Uri.CREATOR.createFromParcel(in); mType = in.readString(); mFlags = in.readInt(); + mPackage = in.readString(); mComponent = ComponentName.readFromParcel(in); int N = in.readInt(); diff --git a/core/java/android/content/IntentSender.aidl b/core/java/android/content/IntentSender.aidl new file mode 100644 index 0000000..741bc8c --- /dev/null +++ b/core/java/android/content/IntentSender.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2008 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.content; + +parcelable IntentSender; diff --git a/core/java/android/content/IntentSender.java b/core/java/android/content/IntentSender.java new file mode 100644 index 0000000..4da49d9 --- /dev/null +++ b/core/java/android/content/IntentSender.java @@ -0,0 +1,255 @@ +/* + * 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 android.content; + +import android.content.Context; +import android.content.Intent; +import android.content.IIntentSender; +import android.content.IIntentReceiver; +import android.os.Bundle; +import android.os.RemoteException; +import android.os.Handler; +import android.os.IBinder; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.AndroidException; + + +/** + * A description of an Intent and target action to perform with it. + * The returned object can be + * handed to other applications so that they can perform the action you + * described on your behalf at a later time. + * + * <p>By giving a IntentSender to another application, + * you are granting it the right to perform the operation you have specified + * as if the other application was yourself (with the same permissions and + * identity). As such, you should be careful about how you build the IntentSender: + * often, for example, the base Intent you supply will have the component + * name explicitly set to one of your own components, to ensure it is ultimately + * sent there and nowhere else. + * + * <p>A IntentSender itself is simply a reference to a token maintained by + * the system describing the original data used to retrieve it. This means + * that, even if its owning application's process is killed, the + * IntentSender itself will remain usable from other processes that + * have been given it. If the creating application later re-retrieves the + * same kind of IntentSender (same operation, same Intent action, data, + * categories, and components, and same flags), it will receive a IntentSender + * representing the same token if that is still valid. + * + */ +public class IntentSender implements Parcelable { + private final IIntentSender mTarget; + + /** + * Exception thrown when trying to send through a PendingIntent that + * has been canceled or is otherwise no longer able to execute the request. + */ + public static class SendIntentException extends AndroidException { + public SendIntentException() { + } + + public SendIntentException(String name) { + super(name); + } + + public SendIntentException(Exception cause) { + super(cause); + } + } + + /** + * Callback interface for discovering when a send operation has + * completed. Primarily for use with a IntentSender that is + * performing a broadcast, this provides the same information as + * calling {@link Context#sendOrderedBroadcast(Intent, String, + * android.content.BroadcastReceiver, Handler, int, String, Bundle) + * Context.sendBroadcast()} with a final BroadcastReceiver. + */ + public interface OnFinished { + /** + * Called when a send operation as completed. + * + * @param IntentSender The IntentSender this operation was sent through. + * @param intent The original Intent that was sent. + * @param resultCode The final result code determined by the send. + * @param resultData The final data collected by a broadcast. + * @param resultExtras The final extras collected by a broadcast. + */ + void onSendFinished(IntentSender IntentSender, Intent intent, + int resultCode, String resultData, Bundle resultExtras); + } + + private static class FinishedDispatcher extends IIntentReceiver.Stub + implements Runnable { + private final IntentSender mIntentSender; + private final OnFinished mWho; + private final Handler mHandler; + private Intent mIntent; + private int mResultCode; + private String mResultData; + private Bundle mResultExtras; + FinishedDispatcher(IntentSender pi, OnFinished who, Handler handler) { + mIntentSender = pi; + mWho = who; + mHandler = handler; + } + public void performReceive(Intent intent, int resultCode, + String data, Bundle extras, boolean serialized) { + mIntent = intent; + mResultCode = resultCode; + mResultData = data; + mResultExtras = extras; + if (mHandler == null) { + run(); + } else { + mHandler.post(this); + } + } + public void run() { + mWho.onSendFinished(mIntentSender, mIntent, mResultCode, + mResultData, mResultExtras); + } + } + + /** + * Perform the operation associated with this IntentSender, allowing the + * caller to specify information about the Intent to use and be notified + * when the send has completed. + * + * @param context The Context of the caller. This may be null if + * <var>intent</var> is also null. + * @param code Result code to supply back to the IntentSender's target. + * @param intent Additional Intent data. See {@link Intent#fillIn + * Intent.fillIn()} for information on how this is applied to the + * original Intent. Use null to not modify the original Intent. + * @param onFinished The object to call back on when the send has + * completed, or null for no callback. + * @param handler Handler identifying the thread on which the callback + * should happen. If null, the callback will happen from the thread + * pool of the process. + * + * + * @throws SendIntentException Throws CanceledIntentException if the IntentSender + * is no longer allowing more intents to be sent through it. + */ + public void sendIntent(Context context, int code, Intent intent, + OnFinished onFinished, Handler handler) throws SendIntentException { + try { + String resolvedType = intent != null ? + intent.resolveTypeIfNeeded(context.getContentResolver()) + : null; + int res = mTarget.send(code, intent, resolvedType, + onFinished != null + ? new FinishedDispatcher(this, onFinished, handler) + : null); + if (res < 0) { + throw new SendIntentException(); + } + } catch (RemoteException e) { + throw new SendIntentException(); + } + } + + /** + * Comparison operator on two IntentSender objects, such that true + * is returned then they both represent the same operation from the + * same package. + */ + @Override + public boolean equals(Object otherObj) { + if (otherObj instanceof IntentSender) { + return mTarget.asBinder().equals(((IntentSender)otherObj) + .mTarget.asBinder()); + } + return false; + } + + @Override + public int hashCode() { + return mTarget.asBinder().hashCode(); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(128); + sb.append("IntentSender{"); + sb.append(Integer.toHexString(System.identityHashCode(this))); + sb.append(": "); + sb.append(mTarget != null ? mTarget.asBinder() : null); + sb.append('}'); + return sb.toString(); + } + + public int describeContents() { + return 0; + } + + public void writeToParcel(Parcel out, int flags) { + out.writeStrongBinder(mTarget.asBinder()); + } + + public static final Parcelable.Creator<IntentSender> CREATOR + = new Parcelable.Creator<IntentSender>() { + public IntentSender createFromParcel(Parcel in) { + IBinder target = in.readStrongBinder(); + return target != null ? new IntentSender(target) : null; + } + + public IntentSender[] newArray(int size) { + return new IntentSender[size]; + } + }; + + /** + * Convenience function for writing either a IntentSender or null pointer to + * a Parcel. You must use this with {@link #readIntentSenderOrNullFromParcel} + * for later reading it. + * + * @param sender The IntentSender to write, or null. + * @param out Where to write the IntentSender. + */ + public static void writeIntentSenderOrNullToParcel(IntentSender sender, + Parcel out) { + out.writeStrongBinder(sender != null ? sender.mTarget.asBinder() + : null); + } + + /** + * Convenience function for reading either a Messenger or null pointer from + * a Parcel. You must have previously written the Messenger with + * {@link #writeIntentSenderOrNullToParcel}. + * + * @param in The Parcel containing the written Messenger. + * + * @return Returns the Messenger read from the Parcel, or null if null had + * been written. + */ + public static IntentSender readIntentSenderOrNullFromParcel(Parcel in) { + IBinder b = in.readStrongBinder(); + return b != null ? new IntentSender(b) : null; + } + + protected IntentSender(IIntentSender target) { + mTarget = target; + } + + protected IntentSender(IBinder target) { + mTarget = IIntentSender.Stub.asInterface(target); + } +} diff --git a/core/java/android/content/SyncAdapter.java b/core/java/android/content/SyncAdapter.java index c658fb7..1d5ade1 100644 --- a/core/java/android/content/SyncAdapter.java +++ b/core/java/android/content/SyncAdapter.java @@ -30,12 +30,12 @@ public abstract class SyncAdapter { public static final int LOG_SYNC_DETAILS = 2743; class Transport extends ISyncAdapter.Stub { - public void startSync(ISyncContext syncContext, Account account, + public void startSync(ISyncContext syncContext, String authority, Account account, Bundle extras) throws RemoteException { SyncAdapter.this.startSync(new SyncContext(syncContext), account, extras); } - public void cancelSync() throws RemoteException { + public void cancelSync(ISyncContext syncContext) throws RemoteException { SyncAdapter.this.cancelSync(); } } diff --git a/core/java/android/content/SyncAdapterNew.java b/core/java/android/content/SyncAdapterNew.java deleted file mode 100644 index 5b23395..0000000 --- a/core/java/android/content/SyncAdapterNew.java +++ /dev/null @@ -1,135 +0,0 @@ -/* - * 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 android.content; - -import android.os.*; -import android.os.Process; -import android.accounts.Account; - -import java.util.concurrent.atomic.AtomicInteger; - -/** - * @hide - */ -public abstract class SyncAdapterNew { - private static final String TAG = "SyncAdapter"; - private final Context mContext; - private final String mAuthority; - - /** Kernel event log tag. Also listed in data/etc/event-log-tags. */ - public static final int LOG_SYNC_DETAILS = 2743; - - public SyncAdapterNew(Context context, String authority) { - mContext = context; - mAuthority = authority; - } - - class Transport extends ISyncAdapter.Stub { - private final AtomicInteger mNumSyncStarts = new AtomicInteger(0); - private volatile Thread mSyncThread; - - public void startSync(ISyncContext syncContext, Account account, Bundle extras) { - boolean alreadyInProgress; - synchronized (this) { - if (mSyncThread == null) { - mSyncThread = new Thread( - new SyncRunnable(new SyncContext(syncContext), account, extras), - "SyncAdapterThread-" + mNumSyncStarts.incrementAndGet()); - Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); - mSyncThread.start(); - alreadyInProgress = false; - } else { - alreadyInProgress = true; - } - } - - if (alreadyInProgress) { - try { - syncContext.onFinished(SyncResult.ALREADY_IN_PROGRESS); - } catch (RemoteException e) { - // don't care if the caller is no longer around - } - } - } - - public void cancelSync() { - synchronized (this) { - if (mSyncThread != null) { - mSyncThread.interrupt(); - } - } - } - - private class SyncRunnable implements Runnable { - private final SyncContext mSyncContext; - private final Account mAccount; - private final Bundle mExtras; - - private SyncRunnable(SyncContext syncContext, Account account, Bundle extras) { - mSyncContext = syncContext; - mAccount = account; - mExtras = extras; - } - - public void run() { - if (isCanceled()) { - return; - } - - SyncResult syncResult = new SyncResult(); - ContentProviderClient provider = mAuthority != null - ? mContext.getContentResolver().acquireContentProviderClient(mAuthority) - : null; - try { - SyncAdapterNew.this.performSync(mAccount, mExtras, provider, syncResult); - } finally { - if (provider != null) { - provider.release(); - } - if (!isCanceled()) { - mSyncContext.onFinished(syncResult); - } - mSyncThread = null; - } - } - - private boolean isCanceled() { - return Thread.currentThread().isInterrupted(); - } - } - } - - Transport mTransport = new Transport(); - - /** - * Get the Transport object. - */ - public final ISyncAdapter getISyncAdapter() { - return mTransport; - } - - /** - * Perform a sync for this account. SyncAdapter-specific parameters may - * be specified in extras, which is guaranteed to not be null. Invocations - * of this method are guaranteed to be serialized. - * - * @param account the account that should be synced - * @param extras SyncAdapter-specific parameters - */ - public abstract void performSync(Account account, Bundle extras, - ContentProviderClient provider, SyncResult syncResult); -}
\ No newline at end of file diff --git a/core/java/android/content/SyncManager.java b/core/java/android/content/SyncManager.java index cba02aa..c7954a5 100644 --- a/core/java/android/content/SyncManager.java +++ b/core/java/android/content/SyncManager.java @@ -33,8 +33,6 @@ import android.content.pm.IPackageManager; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.pm.RegisteredServicesCache; -import android.database.Cursor; -import android.database.DatabaseUtils; import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.net.Uri; @@ -50,10 +48,7 @@ import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemClock; import android.os.SystemProperties; -import android.provider.Sync; import android.provider.Settings; -import android.provider.Sync.History; -import android.text.TextUtils; import android.text.format.DateUtils; import android.text.format.Time; import android.util.Config; @@ -77,8 +72,6 @@ import java.util.List; import java.util.Map; import java.util.PriorityQueue; import java.util.Random; -import java.util.Observer; -import java.util.Observable; import java.util.Set; /** @@ -1435,7 +1428,7 @@ class SyncManager implements OnAccountsUpdatedListener { // outstanding if (mActiveSyncContext.mSyncAdapter != null) { try { - mActiveSyncContext.mSyncAdapter.cancelSync(); + mActiveSyncContext.mSyncAdapter.cancelSync(mActiveSyncContext); } catch (RemoteException e) { // we don't need to retry this in this case } @@ -1678,8 +1671,8 @@ class SyncManager implements OnAccountsUpdatedListener { mActiveSyncContext.mSyncAdapter = syncAdapter; final SyncOperation syncOperation = mActiveSyncContext.mSyncOperation; try { - syncAdapter.startSync(mActiveSyncContext, syncOperation.account, - syncOperation.extras); + syncAdapter.startSync(mActiveSyncContext, syncOperation.authority, + syncOperation.account, syncOperation.extras); } catch (RemoteException remoteExc) { if (Config.LOGD) { Log.d(TAG, "runStateIdle: caught a RemoteException, rescheduling", remoteExc); @@ -1742,7 +1735,7 @@ class SyncManager implements OnAccountsUpdatedListener { } if (activeSyncContext.mSyncAdapter != null) { try { - activeSyncContext.mSyncAdapter.cancelSync(); + activeSyncContext.mSyncAdapter.cancelSync(activeSyncContext); } catch (RemoteException e) { // we don't need to retry this in this case } diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java index f10dd53..2a2cf93 100644 --- a/core/java/android/content/pm/ApplicationInfo.java +++ b/core/java/android/content/pm/ApplicationInfo.java @@ -137,6 +137,13 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { public static final int FLAG_TEST_ONLY = 1<<8; /** + * Value for {@link #flags}: true when the application's window can be + * expanded over default window size in target density (320x480 for + * 1.0 density, 480x720 for 1.5 density etc) + */ + public static final int FLAG_SUPPORTS_LARGE_SCREENS = 1<<9; + + /** * Value for {@link #flags}: this is false if the application has set * its android:allowBackup to false, true otherwise. * @@ -201,12 +208,6 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { public int[] supportsDensities; /** - * True when the application's window can be expanded over default window - * size in target density (320x480 for 1.0 density, 480x720 for 1.5 density etc) - */ - public boolean expandable = false; - - /** * The minimum SDK version this application targets. It may run on earilier * versions, but it knows how to work with any new behavior added at this * version. Will be {@link android.os.Build.VERSION_CODES#CUR_DEVELOPMENT} @@ -240,7 +241,6 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { pw.println(prefix + "manageSpaceActivityName="+manageSpaceActivityName); pw.println(prefix + "description=0x"+Integer.toHexString(descriptionRes)); pw.println(prefix + "supportsDensities=" + supportsDensities); - pw.println(prefix + "expandable=" + expandable); super.dumpBack(pw, prefix); } @@ -288,7 +288,6 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { manageSpaceActivityName = orig.manageSpaceActivityName; descriptionRes = orig.descriptionRes; supportsDensities = orig.supportsDensities; - expandable = orig.expandable; } @@ -321,7 +320,6 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { dest.writeString(backupAgentName); dest.writeInt(descriptionRes); dest.writeIntArray(supportsDensities); - dest.writeInt(expandable ? 1 : 0); } public static final Parcelable.Creator<ApplicationInfo> CREATOR @@ -353,7 +351,6 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { backupAgentName = source.readString(); descriptionRes = source.readInt(); supportsDensities = source.createIntArray(); - expandable = source.readInt() != 0; } /** @@ -383,7 +380,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { * @hide */ public void disableCompatibilityMode() { - expandable = true; + flags |= FLAG_SUPPORTS_LARGE_SCREENS; supportsDensities = ANY_DENSITIES_ARRAY; } } diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl index 9eca4a5..68f8417 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -34,7 +34,7 @@ import android.content.pm.PermissionInfo; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; import android.net.Uri; -import android.app.PendingIntent; +import android.content.IntentSender; /** * See {@link PackageManager} for documentation on most of the APIs @@ -81,9 +81,6 @@ interface IPackageManager { ResolveInfo resolveIntent(in Intent intent, String resolvedType, int flags); - ResolveInfo resolveIntentForPackage(in Intent intent, String resolvedType, int flags, - String packageName); - List<ResolveInfo> queryIntentActivities(in Intent intent, String resolvedType, int flags); @@ -238,12 +235,12 @@ interface IPackageManager { * and the current free storage is YY, * if XX is less than YY, just return. if not free XX-YY number * of bytes if possible. - * @param opFinishedIntent PendingIntent call back used to + * @param pi IntentSender call back used to * notify when the operation is completed.May be null * to indicate that no call back is desired. */ void freeStorage(in long freeStorageSize, - in PendingIntent opFinishedIntent); + in IntentSender pi); /** * Delete all the cache files in an applications cache directory @@ -280,4 +277,11 @@ interface IPackageManager { boolean isSafeMode(); void systemReady(); boolean hasSystemUidErrors(); + + /** + * Ask the package manager to perform dex-opt (if needed) on the given + * package, if it already hasn't done mode. Only does this if running + * in the special development "no pre-dexopt" mode. + */ + boolean performDexOpt(String packageName); } diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 88eccf7..3250a87 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -16,12 +16,11 @@ package android.content.pm; - -import android.app.PendingIntent; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.IntentSender; import android.content.res.Resources; import android.content.res.XmlResourceParser; import android.graphics.drawable.Drawable; @@ -180,12 +179,6 @@ public abstract class PackageManager { public static final int MATCH_DEFAULT_ONLY = 0x00010000; /** - * {@link ApplicationInfo} flag: return the - * {link ApplicationInfo#expandable} boolean flag of the package. - */ - public static final int GET_EXPANDABLE = 0x00020000; - - /** * Permission check result: this is returned by {@link #checkPermission} * if the permission has been granted to the given package. */ @@ -985,23 +978,6 @@ public abstract class PackageManager { public abstract ResolveInfo resolveActivity(Intent intent, int flags); /** - * Resolve the intent restricted to a package. - * {@see #resolveActivity} - * - * @param intent An intent containing all of the desired specification - * (action, data, type, category, and/or component). - * @param flags Additional option flags. The most important is - * MATCH_DEFAULT_ONLY, to limit the resolution to only - * those activities that support the CATEGORY_DEFAULT. - * @param packageName Restrict the intent resolution to this package. - * - * @return Returns a ResolveInfo containing the final activity intent that - * was determined to be the best action. Returns null if no - * matching activity was found. - */ - public abstract ResolveInfo resolveActivity(Intent intent, int flags, String packageName); - - /** * Retrieve all activities that can be performed for the given intent. * * @param intent The desired intent as per resolveActivity(). @@ -1520,7 +1496,7 @@ public abstract class PackageManager { * @hide */ public abstract void freeStorageAndNotify(long freeStorageSize, IPackageDataObserver observer); - + /** * Free storage by deleting LRU sorted list of cache files across * all applications. If the currently available free storage @@ -1538,13 +1514,13 @@ public abstract class PackageManager { * and the current free storage is YY, * if XX is less than YY, just return. if not free XX-YY number * of bytes if possible. - * @param opFinishedIntent PendingIntent call back used to + * @param pi IntentSender call back used to * notify when the operation is completed.May be null * to indicate that no call back is desired. * * @hide */ - public abstract void freeStorage(long freeStorageSize, PendingIntent opFinishedIntent); + public abstract void freeStorage(long freeStorageSize, IntentSender pi); /** * Retrieve the size information for a package. diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index ab8559c..ab9518e 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -777,7 +777,7 @@ public class PackageParser { targetCode = minCode = val.string.toString(); } else { // If it's not a string, it's an integer. - minVers = val.data; + targetVers = minVers = val.data; } } @@ -798,6 +798,25 @@ public class PackageParser { sa.recycle(); + if (minCode != null) { + if (!minCode.equals(mSdkCodename)) { + if (mSdkCodename != null) { + outError[0] = "Requires development platform " + minCode + + " (current platform is " + mSdkCodename + ")"; + } else { + outError[0] = "Requires development platform " + minCode + + " but this is a release platform."; + } + mParseError = PackageManager.INSTALL_FAILED_OLDER_SDK; + return null; + } + } else if (minVers > mSdkVersion) { + outError[0] = "Requires newer sdk version #" + minVers + + " (current version is #" + mSdkVersion + ")"; + mParseError = PackageManager.INSTALL_FAILED_OLDER_SDK; + return null; + } + if (targetCode != null) { if (!targetCode.equals(mSdkCodename)) { if (mSdkCodename != null) { @@ -817,13 +836,6 @@ public class PackageParser { pkg.applicationInfo.targetSdkVersion = targetVers; } - if (minVers > mSdkVersion) { - outError[0] = "Requires newer sdk version #" + minVers - + " (current version is #" + mSdkVersion + ")"; - mParseError = PackageManager.INSTALL_FAILED_OLDER_SDK; - return null; - } - if (maxVers < mSdkVersion) { outError[0] = "Requires older sdk version #" + maxVers + " (current version is #" + mSdkVersion + ")"; @@ -865,7 +877,7 @@ public class PackageParser { XmlUtils.skipCurrentTag(parser); } else if (tagName.equals("expandable")) { - pkg.expandable = true; + pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS; XmlUtils.skipCurrentTag(parser); } else { Log.w(TAG, "Bad element under <manifest>: " @@ -2262,9 +2274,6 @@ public class PackageParser { public final ArrayList<Integer> supportsDensityList = new ArrayList<Integer>(); public int[] supportsDensities = null; - // If the application's window is expandable. - public boolean expandable; - // If this is a 3rd party app, this is the path of the zip file. public String mPath; @@ -2287,6 +2296,17 @@ public class PackageParser { // preferred up order. public int mPreferredOrder = 0; + // For use by package manager service to keep track of which apps + // have been installed with forward locking. + public boolean mForwardLocked; + + // For use by the package manager to keep track of the path to the + // file an app came from. + public String mScanPath; + + // For use by package manager to keep track of where it has done dexopt. + public boolean mDidDexOpt; + // Additional data supplied by callers. public Object mExtras; @@ -2439,9 +2459,6 @@ public class PackageParser { && p.supportsDensities != null) { return true; } - if ((flags & PackageManager.GET_EXPANDABLE) != 0) { - return true; - } return false; } @@ -2462,9 +2479,6 @@ public class PackageParser { if ((flags & PackageManager.GET_SUPPORTS_DENSITIES) != 0) { ai.supportsDensities = p.supportsDensities; } - if ((flags & PackageManager.GET_EXPANDABLE) != 0) { - ai.expandable = p.expandable; - } return ai; } diff --git a/core/java/android/content/res/CompatibilityInfo.java b/core/java/android/content/res/CompatibilityInfo.java index 836de39..680fef8 100644 --- a/core/java/android/content/res/CompatibilityInfo.java +++ b/core/java/android/content/res/CompatibilityInfo.java @@ -69,7 +69,8 @@ public class CompatibilityInfo { public final boolean mScalingRequired; public CompatibilityInfo(ApplicationInfo appInfo) { - mExpandable = mConfiguredExpandable = appInfo.expandable; + mExpandable = mConfiguredExpandable = + (appInfo.flags & ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS) != 0; float packageDensityScale = -1.0f; if (appInfo.supportsDensities != null) { diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java index 2f63820..71dbd38 100644 --- a/core/java/android/content/res/Resources.java +++ b/core/java/android/content/res/Resources.java @@ -22,11 +22,9 @@ import com.android.internal.util.XmlUtils; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; -import android.app.ActivityThread.PackageInfo; import android.content.pm.ApplicationInfo; import android.graphics.Movie; import android.graphics.drawable.Drawable; -import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.ColorDrawable; import android.os.Bundle; import android.os.SystemProperties; @@ -35,6 +33,7 @@ import android.util.DisplayMetrics; import android.util.Log; import android.util.SparseArray; import android.util.TypedValue; +import android.util.LongSparseArray; import java.io.IOException; import java.io.InputStream; @@ -59,19 +58,19 @@ public class Resources { // Information about preloaded resources. Note that they are not // protected by a lock, because while preloading in zygote we are all // single-threaded, and after that these are immutable. - private static final SparseArray<Drawable.ConstantState> sPreloadedDrawables - = new SparseArray<Drawable.ConstantState>(); + private static final LongSparseArray<Drawable.ConstantState> sPreloadedDrawables + = new LongSparseArray<Drawable.ConstantState>(); private static final SparseArray<ColorStateList> mPreloadedColorStateLists = new SparseArray<ColorStateList>(); private static boolean mPreloaded; - private final SparseArray<Drawable.ConstantState> mPreloadedDrawables; + private final LongSparseArray<Drawable.ConstantState> mPreloadedDrawables; /*package*/ final TypedValue mTmpValue = new TypedValue(); // These are protected by the mTmpValue lock. - private final SparseArray<WeakReference<Drawable.ConstantState> > mDrawableCache - = new SparseArray<WeakReference<Drawable.ConstantState> >(); + private final LongSparseArray<WeakReference<Drawable.ConstantState> > mDrawableCache + = new LongSparseArray<WeakReference<Drawable.ConstantState> >(); private final SparseArray<WeakReference<ColorStateList> > mColorStateListCache = new SparseArray<WeakReference<ColorStateList> >(); private boolean mPreloading; @@ -89,20 +88,20 @@ public class Resources { private final CompatibilityInfo mCompatibilityInfo; - private static final SparseArray<Object> EMPTY_ARRAY = new SparseArray<Object>() { + private static final LongSparseArray<Object> EMPTY_ARRAY = new LongSparseArray<Object>() { @Override - public void put(int k, Object o) { + public void put(long k, Object o) { throw new UnsupportedOperationException(); } @Override - public void append(int k, Object o) { + public void append(long k, Object o) { throw new UnsupportedOperationException(); } }; @SuppressWarnings("unchecked") - private static <T> SparseArray<T> emptySparseArray() { - return (SparseArray<T>) EMPTY_ARRAY; + private static <T> LongSparseArray<T> emptySparseArray() { + return (LongSparseArray<T>) EMPTY_ARRAY; } /** @@ -1315,14 +1314,14 @@ public class Resources { configChanges, cs.getChangingConfigurations())) { if (DEBUG_CONFIG) { Log.d(TAG, "FLUSHING #0x" - + Integer.toHexString(mDrawableCache.keyAt(i)) + + Long.toHexString(mDrawableCache.keyAt(i)) + " / " + cs + " with changes: 0x" + Integer.toHexString(cs.getChangingConfigurations())); } mDrawableCache.setValueAt(i, null); } else if (DEBUG_CONFIG) { Log.d(TAG, "(Keeping #0x" - + Integer.toHexString(mDrawableCache.keyAt(i)) + + Long.toHexString(mDrawableCache.keyAt(i)) + " / " + cs + " with changes: 0x" + Integer.toHexString(cs.getChangingConfigurations()) + ")"); @@ -1653,7 +1652,7 @@ public class Resources { } } - final int key = (value.assetCookie << 24) | value.data; + final long key = (((long) value.assetCookie) << 32) | value.data; Drawable dr = getCachedDrawable(key); if (dr != null) { @@ -1733,7 +1732,7 @@ public class Resources { return dr; } - private Drawable getCachedDrawable(int key) { + private Drawable getCachedDrawable(long key) { synchronized (mTmpValue) { WeakReference<Drawable.ConstantState> wr = mDrawableCache.get(key); if (wr != null) { // we have the key diff --git a/core/java/android/gesture/GestureOverlayView.java b/core/java/android/gesture/GestureOverlayView.java index fd6d850..5bfdcc4 100755 --- a/core/java/android/gesture/GestureOverlayView.java +++ b/core/java/android/gesture/GestureOverlayView.java @@ -84,6 +84,7 @@ public class GestureOverlayView extends FrameLayout { private final Rect mInvalidRect = new Rect(); private final Path mPath = new Path(); + private boolean mGestureVisible = true; private float mX; private float mY; @@ -274,14 +275,6 @@ public class GestureOverlayView extends FrameLayout { return mCurrentGesture; } - public long getFadeOffset() { - return mFadeOffset; - } - - public void setFadeOffset(long fadeOffset) { - mFadeOffset = fadeOffset; - } - public void setGesture(Gesture gesture) { if (mCurrentGesture != null) { clear(false); @@ -304,6 +297,31 @@ public class GestureOverlayView extends FrameLayout { invalidate(); } + public Path getGesturePath() { + return mPath; + } + + public Path getGesturePath(Path path) { + path.set(mPath); + return path; + } + + public boolean isGestureVisible() { + return mGestureVisible; + } + + public void setGestureVisible(boolean visible) { + mGestureVisible = visible; + } + + public long getFadeOffset() { + return mFadeOffset; + } + + public void setFadeOffset(long fadeOffset) { + mFadeOffset = fadeOffset; + } + public void addOnGestureListener(OnGestureListener listener) { mOnGestureListeners.add(listener); } @@ -372,7 +390,7 @@ public class GestureOverlayView extends FrameLayout { public void draw(Canvas canvas) { super.draw(canvas); - if (mCurrentGesture != null) { + if (mCurrentGesture != null && mGestureVisible) { canvas.drawPath(mPath, mGesturePaint); } } diff --git a/core/java/android/os/MemoryFile.java b/core/java/android/os/MemoryFile.java index c14925c..03542dd 100644 --- a/core/java/android/os/MemoryFile.java +++ b/core/java/android/os/MemoryFile.java @@ -52,7 +52,7 @@ public class MemoryFile private static native void native_write(FileDescriptor fd, int address, byte[] buffer, int srcOffset, int destOffset, int count, boolean isUnpinned) throws IOException; private static native void native_pin(FileDescriptor fd, boolean pin) throws IOException; - private static native boolean native_is_ashmem_region(FileDescriptor fd) throws IOException; + private static native int native_get_mapped_size(FileDescriptor fd) throws IOException; private FileDescriptor mFD; // ashmem file descriptor private int mAddress; // address of ashmem memory @@ -300,7 +300,20 @@ public class MemoryFile * @hide */ public static boolean isMemoryFile(FileDescriptor fd) throws IOException { - return native_is_ashmem_region(fd); + return (native_get_mapped_size(fd) >= 0); + } + + /** + * Returns the size of the memory file, rounded up to a page boundary, that + * the file descriptor refers to, or -1 if the file descriptor does not + * refer to a memory file. + * + * @throws IOException If <code>fd</code> is not a valid file descriptor. + * + * @hide + */ + public static int getMappedSize(FileDescriptor fd) throws IOException { + return native_get_mapped_size(fd); } /** diff --git a/core/java/android/preference/PreferenceScreen.java b/core/java/android/preference/PreferenceScreen.java index 5353b53..6ea2528 100644 --- a/core/java/android/preference/PreferenceScreen.java +++ b/core/java/android/preference/PreferenceScreen.java @@ -22,6 +22,7 @@ import android.content.DialogInterface; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; +import android.text.TextUtils; import android.util.AttributeSet; import android.view.View; import android.widget.Adapter; @@ -147,13 +148,20 @@ public final class PreferenceScreen extends PreferenceGroup implements AdapterVi ListView listView = new ListView(context); bind(listView); - Dialog dialog = mDialog = new Dialog(context, com.android.internal.R.style.Theme_NoTitleBar); + // Set the title bar if title is available, else no title bar + final CharSequence title = getTitle(); + Dialog dialog = mDialog = new Dialog(context, !TextUtils.isEmpty(title) + ? com.android.internal.R.style.Theme_NoTitleBar + : com.android.internal.R.style.Theme); dialog.setContentView(listView); + if (!TextUtils.isEmpty(title)) { + dialog.setTitle(title); + } dialog.setOnDismissListener(this); if (state != null) { dialog.onRestoreInstanceState(state); } - + // Add the screen to the list of preferences screens opened as dialogs getPreferenceManager().addPreferencesScreen(dialog); diff --git a/core/java/android/provider/Browser.java b/core/java/android/provider/Browser.java index 9c8c537..94254de 100644 --- a/core/java/android/provider/Browser.java +++ b/core/java/android/provider/Browser.java @@ -96,7 +96,7 @@ public class Browser { public static final String[] HISTORY_PROJECTION = new String[] { BookmarkColumns._ID, BookmarkColumns.URL, BookmarkColumns.VISITS, BookmarkColumns.DATE, BookmarkColumns.BOOKMARK, BookmarkColumns.TITLE, - BookmarkColumns.FAVICON }; + BookmarkColumns.FAVICON, BookmarkColumns.THUMBNAIL }; /* these indices dependent on HISTORY_PROJECTION */ public static final int HISTORY_PROJECTION_ID_INDEX = 0; @@ -106,6 +106,10 @@ public class Browser { public static final int HISTORY_PROJECTION_BOOKMARK_INDEX = 4; public static final int HISTORY_PROJECTION_TITLE_INDEX = 5; public static final int HISTORY_PROJECTION_FAVICON_INDEX = 6; + /** + * @hide + */ + public static final int HISTORY_PROJECTION_THUMBNAIL_INDEX = 7; /* columns needed to determine whether to truncate history */ public static final String[] TRUNCATE_HISTORY_PROJECTION = new String[] { @@ -486,6 +490,10 @@ public class Browser { public static final String TITLE = "title"; public static final String CREATED = "created"; public static final String FAVICON = "favicon"; + /** + * @hide + */ + public static final String THUMBNAIL = "thumbnail"; } public static class SearchColumns implements BaseColumns { diff --git a/core/java/android/provider/Contacts.java b/core/java/android/provider/Contacts.java index 0829cfb..5f84e57 100644 --- a/core/java/android/provider/Contacts.java +++ b/core/java/android/provider/Contacts.java @@ -40,7 +40,7 @@ import java.io.InputStream; */ public class Contacts { private static final String TAG = "Contacts"; - + public static final String AUTHORITY = "contacts"; /** @@ -191,7 +191,7 @@ public class Contacts { * <p>Type: TEXT</P> */ public static final String PHONETIC_NAME = "phonetic_name"; - + /** * The display name. If name is not null name, else if number is not null number, * else if email is not null email. @@ -206,7 +206,7 @@ public class Contacts { * @hide Used only in Contacts application for now. */ public static final String SORT_STRING = "sort_string"; - + /** * Notes about the person. * <P>Type: TEXT</P> @@ -248,7 +248,7 @@ public class Contacts { * The server version of the photo * <P>Type: TEXT (the version number portion of the photo URI)</P> */ - public static final String PHOTO_VERSION = "photo_version"; + public static final String PHOTO_VERSION = "photo_version"; } /** @@ -287,14 +287,14 @@ public class Contacts { * additional path segment after this URI. This matches any people with * at least one E-mail or IM {@link ContactMethods} that match the * filter. - * + * * Not exposed because we expect significant changes in the contacts * schema and do not want to have to support this. * @hide */ public static final Uri WITH_EMAIL_OR_IM_FILTER_URI = Uri.parse("content://contacts/people/with_email_or_im_filter"); - + /** * The MIME type of {@link #CONTENT_URI} providing a directory of * people. @@ -379,13 +379,13 @@ public class Contacts { if (groupId == 0) { throw new IllegalStateException("Failed to find the My Contacts group"); } - + return addToGroup(resolver, personId, groupId); } /** * Adds a person to a group referred to by name. - * + * * @param resolver the resolver to use * @param personId the person to add to the group * @param groupName the name of the group to add the contact to @@ -409,13 +409,13 @@ public class Contacts { if (groupId == 0) { throw new IllegalStateException("Failed to find the My Contacts group"); } - + return addToGroup(resolver, personId, groupId); } /** * Adds a person to a group. - * + * * @param resolver the resolver to use * @param personId the person to add to the group * @param groupId the group to add the person to @@ -427,14 +427,14 @@ public class Contacts { values.put(GroupMembership.GROUP_ID, groupId); return resolver.insert(GroupMembership.CONTENT_URI, values); } - + private static final String[] GROUPS_PROJECTION = new String[] { Groups._ID, }; /** * Creates a new contacts and adds it to the "My Contacts" group. - * + * * @param resolver the ContentResolver to use * @param values the values to use when creating the contact * @return the URI of the contact, or null if the operation fails @@ -472,7 +472,7 @@ public class Contacts { values.put(Photos.DATA, data); cr.update(photoUri, values, null, null); } - + /** * Opens an InputStream for the person's photo and returns the photo as a Bitmap. * If the person's photo isn't present returns the placeholderImageResource instead. @@ -739,7 +739,7 @@ public class Contacts { CharSequence display = ""; if (type != People.Phones.TYPE_CUSTOM) { - CharSequence[] labels = labelArray != null? labelArray + CharSequence[] labels = labelArray != null? labelArray : context.getResources().getTextArray( com.android.internal.R.array.phoneTypes); try { @@ -759,7 +759,7 @@ public class Contacts { CharSequence label) { return getDisplayLabel(context, type, label, null); } - + /** * The content:// style URL for this table */ @@ -984,7 +984,7 @@ public class Contacts { throw new IllegalArgumentException( "the value is not a valid encoded protocol, " + encodedString); } - + /** * This looks up the provider name defined in * {@link android.provider.Im.ProviderNames} from the predefined IM protocol id. @@ -1197,7 +1197,7 @@ public class Contacts { /** * Gets the resource ID for the proper presence icon. - * + * * @param status the status to get the icon for * @return the resource ID for the proper presence icon */ @@ -1205,17 +1205,17 @@ public class Contacts { switch (status) { case Contacts.People.AVAILABLE: return com.android.internal.R.drawable.presence_online; - + case Contacts.People.IDLE: case Contacts.People.AWAY: return com.android.internal.R.drawable.presence_away; - + case Contacts.People.DO_NOT_DISTURB: return com.android.internal.R.drawable.presence_busy; - + case Contacts.People.INVISIBLE: return com.android.internal.R.drawable.presence_invisible; - + case Contacts.People.OFFLINE: default: return com.android.internal.R.drawable.presence_offline; @@ -1455,28 +1455,27 @@ public class Contacts { * This is the intent that is fired when a search suggestion is clicked on. */ public static final String SEARCH_SUGGESTION_CLICKED = - "android.provider.Contacts.SEARCH_SUGGESTION_CLICKED"; + ContactsContract.Intents.SEARCH_SUGGESTION_CLICKED; /** - * This is the intent that is fired when a search suggestion for dialing a number + * This is the intent that is fired when a search suggestion for dialing a number * is clicked on. */ public static final String SEARCH_SUGGESTION_DIAL_NUMBER_CLICKED = - "android.provider.Contacts.SEARCH_SUGGESTION_DIAL_NUMBER_CLICKED"; + ContactsContract.Intents.SEARCH_SUGGESTION_DIAL_NUMBER_CLICKED; /** * This is the intent that is fired when a search suggestion for creating a contact * is clicked on. */ public static final String SEARCH_SUGGESTION_CREATE_CONTACT_CLICKED = - "android.provider.Contacts.SEARCH_SUGGESTION_CREATE_CONTACT_CLICKED"; + ContactsContract.Intents.SEARCH_SUGGESTION_CREATE_CONTACT_CLICKED; /** * Starts an Activity that lets the user pick a contact to attach an image to. * After picking the contact it launches the image cropper in face detection mode. */ - public static final String ATTACH_IMAGE = - "com.android.contacts.action.ATTACH_IMAGE"; + public static final String ATTACH_IMAGE = ContactsContract.Intents.ATTACH_IMAGE; /** * Takes as input a data URI with a mailto: or tel: scheme. If a single @@ -1502,7 +1501,7 @@ public class Contacts { * prompting the user when the contact doesn't exist. */ public static final String SHOW_OR_CREATE_CONTACT = - "com.android.contacts.action.SHOW_OR_CREATE_CONTACT"; + ContactsContract.Intents.SHOW_OR_CREATE_CONTACT; /** * Used with {@link #SHOW_OR_CREATE_CONTACT} to force creating a new @@ -1511,9 +1510,8 @@ public class Contacts { * <p> * Type: BOOLEAN */ - public static final String EXTRA_FORCE_CREATE = - "com.android.contacts.action.FORCE_CREATE"; - + public static final String EXTRA_FORCE_CREATE = ContactsContract.Intents.EXTRA_FORCE_CREATE; + /** * Used with {@link #SHOW_OR_CREATE_CONTACT} to specify an exact * description to be shown when prompting user about creating a new @@ -1522,7 +1520,7 @@ public class Contacts { * Type: STRING */ public static final String EXTRA_CREATE_DESCRIPTION = - "com.android.contacts.action.CREATE_DESCRIPTION"; + ContactsContract.Intents.EXTRA_CREATE_DESCRIPTION; /** * Intents related to the Contacts app UI. @@ -1531,43 +1529,42 @@ public class Contacts { /** * The action for the default contacts list tab. */ - public static final String LIST_DEFAULT = - "com.android.contacts.action.LIST_DEFAULT"; + public static final String LIST_DEFAULT = ContactsContract.Intents.UI.LIST_DEFAULT; /** * The action for the contacts list tab. */ public static final String LIST_GROUP_ACTION = - "com.android.contacts.action.LIST_GROUP"; + ContactsContract.Intents.UI.LIST_GROUP_ACTION; /** * When in LIST_GROUP_ACTION mode, this is the group to display. */ - public static final String GROUP_NAME_EXTRA_KEY = "com.android.contacts.extra.GROUP"; - + public static final String GROUP_NAME_EXTRA_KEY = + ContactsContract.Intents.UI.GROUP_NAME_EXTRA_KEY; /** * The action for the all contacts list tab. */ public static final String LIST_ALL_CONTACTS_ACTION = - "com.android.contacts.action.LIST_ALL_CONTACTS"; + ContactsContract.Intents.UI.LIST_ALL_CONTACTS_ACTION; /** * The action for the contacts with phone numbers list tab. */ public static final String LIST_CONTACTS_WITH_PHONES_ACTION = - "com.android.contacts.action.LIST_CONTACTS_WITH_PHONES"; + ContactsContract.Intents.UI.LIST_CONTACTS_WITH_PHONES_ACTION; /** * The action for the starred contacts list tab. */ public static final String LIST_STARRED_ACTION = - "com.android.contacts.action.LIST_STARRED"; + ContactsContract.Intents.UI.LIST_STARRED_ACTION; /** * The action for the frequent contacts list tab. */ public static final String LIST_FREQUENT_ACTION = - "com.android.contacts.action.LIST_FREQUENT"; + ContactsContract.Intents.UI.LIST_FREQUENT_ACTION; /** * The action for the "strequent" contacts list tab. It first lists the starred @@ -1575,15 +1572,15 @@ public class Contacts { * order of the number of times they have been contacted. */ public static final String LIST_STREQUENT_ACTION = - "com.android.contacts.action.LIST_STREQUENT"; + ContactsContract.Intents.UI.LIST_STREQUENT_ACTION; /** * A key for to be used as an intent extra to set the activity * title to a custom String value. */ public static final String TITLE_EXTRA_KEY = - "com.android.contacts.extra.TITLE_EXTRA"; - + ContactsContract.Intents.UI.TITLE_EXTRA_KEY; + /** * Activity Action: Display a filtered list of contacts * <p> @@ -1592,15 +1589,15 @@ public class Contacts { * <p> * Output: Nothing. */ - public static final String FILTER_CONTACTS_ACTION = - "com.android.contacts.action.FILTER_CONTACTS"; - + public static final String FILTER_CONTACTS_ACTION = + ContactsContract.Intents.UI.FILTER_CONTACTS_ACTION; + /** * Used as an int extra field in {@link #FILTER_CONTACTS_ACTION} * intents to supply the text on which to filter. */ - public static final String FILTER_TEXT_EXTRA_KEY = - "com.android.contacts.extra.FILTER_TEXT"; + public static final String FILTER_TEXT_EXTRA_KEY = + ContactsContract.Intents.UI.FILTER_TEXT_EXTRA_KEY; } /** @@ -1609,170 +1606,179 @@ public class Contacts { */ public static final class Insert { /** The action code to use when adding a contact */ - public static final String ACTION = Intent.ACTION_INSERT; - + public static final String ACTION = ContactsContract.Intents.Insert.ACTION; /** * If present, forces a bypass of quick insert mode. */ - public static final String FULL_MODE = "full_mode"; - + public static final String FULL_MODE = ContactsContract.Intents.Insert.FULL_MODE; /** * The extra field for the contact name. * <P>Type: String</P> */ - public static final String NAME = "name"; + public static final String NAME = ContactsContract.Intents.Insert.NAME; /** * The extra field for the contact phonetic name. * <P>Type: String</P> */ - public static final String PHONETIC_NAME = "phonetic_name"; + public static final String PHONETIC_NAME = + ContactsContract.Intents.Insert.PHONETIC_NAME; /** * The extra field for the contact company. * <P>Type: String</P> */ - public static final String COMPANY = "company"; + public static final String COMPANY = ContactsContract.Intents.Insert.COMPANY; /** * The extra field for the contact job title. * <P>Type: String</P> */ - public static final String JOB_TITLE = "job_title"; + public static final String JOB_TITLE = ContactsContract.Intents.Insert.JOB_TITLE; /** * The extra field for the contact notes. * <P>Type: String</P> */ - public static final String NOTES = "notes"; + public static final String NOTES = ContactsContract.Intents.Insert.NOTES; /** * The extra field for the contact phone number. * <P>Type: String</P> */ - public static final String PHONE = "phone"; + public static final String PHONE = ContactsContract.Intents.Insert.PHONE; /** * The extra field for the contact phone number type. * <P>Type: Either an integer value from {@link android.provider.Contacts.PhonesColumns PhonesColumns}, * or a string specifying a custom label.</P> */ - public static final String PHONE_TYPE = "phone_type"; + public static final String PHONE_TYPE = ContactsContract.Intents.Insert.PHONE_TYPE; /** * The extra field for the phone isprimary flag. * <P>Type: boolean</P> */ - public static final String PHONE_ISPRIMARY = "phone_isprimary"; + public static final String PHONE_ISPRIMARY = + ContactsContract.Intents.Insert.PHONE_ISPRIMARY; /** * The extra field for an optional second contact phone number. * <P>Type: String</P> */ - public static final String SECONDARY_PHONE = "secondary_phone"; + public static final String SECONDARY_PHONE = + ContactsContract.Intents.Insert.SECONDARY_PHONE; /** * The extra field for an optional second contact phone number type. * <P>Type: Either an integer value from {@link android.provider.Contacts.PhonesColumns PhonesColumns}, * or a string specifying a custom label.</P> */ - public static final String SECONDARY_PHONE_TYPE = "secondary_phone_type"; + public static final String SECONDARY_PHONE_TYPE = + ContactsContract.Intents.Insert.SECONDARY_PHONE_TYPE; /** * The extra field for an optional third contact phone number. * <P>Type: String</P> */ - public static final String TERTIARY_PHONE = "tertiary_phone"; + public static final String TERTIARY_PHONE = + ContactsContract.Intents.Insert.TERTIARY_PHONE; /** * The extra field for an optional third contact phone number type. * <P>Type: Either an integer value from {@link android.provider.Contacts.PhonesColumns PhonesColumns}, * or a string specifying a custom label.</P> */ - public static final String TERTIARY_PHONE_TYPE = "tertiary_phone_type"; + public static final String TERTIARY_PHONE_TYPE = + ContactsContract.Intents.Insert.TERTIARY_PHONE_TYPE; /** * The extra field for the contact email address. * <P>Type: String</P> */ - public static final String EMAIL = "email"; + public static final String EMAIL = ContactsContract.Intents.Insert.EMAIL; /** * The extra field for the contact email type. * <P>Type: Either an integer value from {@link android.provider.Contacts.ContactMethodsColumns ContactMethodsColumns} * or a string specifying a custom label.</P> */ - public static final String EMAIL_TYPE = "email_type"; + public static final String EMAIL_TYPE = ContactsContract.Intents.Insert.EMAIL_TYPE; /** * The extra field for the email isprimary flag. * <P>Type: boolean</P> */ - public static final String EMAIL_ISPRIMARY = "email_isprimary"; + public static final String EMAIL_ISPRIMARY = + ContactsContract.Intents.Insert.EMAIL_ISPRIMARY; /** * The extra field for an optional second contact email address. * <P>Type: String</P> */ - public static final String SECONDARY_EMAIL = "secondary_email"; + public static final String SECONDARY_EMAIL = + ContactsContract.Intents.Insert.SECONDARY_EMAIL; /** * The extra field for an optional second contact email type. * <P>Type: Either an integer value from {@link android.provider.Contacts.ContactMethodsColumns ContactMethodsColumns} * or a string specifying a custom label.</P> */ - public static final String SECONDARY_EMAIL_TYPE = "secondary_email_type"; + public static final String SECONDARY_EMAIL_TYPE = + ContactsContract.Intents.Insert.SECONDARY_EMAIL_TYPE; /** * The extra field for an optional third contact email address. * <P>Type: String</P> */ - public static final String TERTIARY_EMAIL = "tertiary_email"; + public static final String TERTIARY_EMAIL = + ContactsContract.Intents.Insert.TERTIARY_EMAIL; /** * The extra field for an optional third contact email type. * <P>Type: Either an integer value from {@link android.provider.Contacts.ContactMethodsColumns ContactMethodsColumns} * or a string specifying a custom label.</P> */ - public static final String TERTIARY_EMAIL_TYPE = "tertiary_email_type"; + public static final String TERTIARY_EMAIL_TYPE = + ContactsContract.Intents.Insert.TERTIARY_EMAIL_TYPE; /** * The extra field for the contact postal address. * <P>Type: String</P> */ - public static final String POSTAL = "postal"; + public static final String POSTAL = ContactsContract.Intents.Insert.POSTAL; /** * The extra field for the contact postal address type. * <P>Type: Either an integer value from {@link android.provider.Contacts.ContactMethodsColumns ContactMethodsColumns} * or a string specifying a custom label.</P> */ - public static final String POSTAL_TYPE = "postal_type"; + public static final String POSTAL_TYPE = ContactsContract.Intents.Insert.POSTAL_TYPE; /** * The extra field for the postal isprimary flag. * <P>Type: boolean</P> */ - public static final String POSTAL_ISPRIMARY = "postal_isprimary"; + public static final String POSTAL_ISPRIMARY = ContactsContract.Intents.Insert.POSTAL_ISPRIMARY; /** * The extra field for an IM handle. * <P>Type: String</P> */ - public static final String IM_HANDLE = "im_handle"; + public static final String IM_HANDLE = ContactsContract.Intents.Insert.IM_HANDLE; /** * The extra field for the IM protocol * <P>Type: the result of {@link Contacts.ContactMethods#encodePredefinedImProtocol} * or {@link Contacts.ContactMethods#encodeCustomImProtocol}.</P> */ - public static final String IM_PROTOCOL = "im_protocol"; + public static final String IM_PROTOCOL = ContactsContract.Intents.Insert.IM_PROTOCOL; /** * The extra field for the IM isprimary flag. * <P>Type: boolean</P> */ - public static final String IM_ISPRIMARY = "im_isprimary"; + public static final String IM_ISPRIMARY = ContactsContract.Intents.Insert.IM_ISPRIMARY; } } } diff --git a/core/java/android/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java index b9c9236..48dc3ae 100644 --- a/core/java/android/provider/ContactsContract.java +++ b/core/java/android/provider/ContactsContract.java @@ -16,6 +16,7 @@ package android.provider; +import android.content.Intent; import android.graphics.BitmapFactory; import android.net.Uri; @@ -179,6 +180,21 @@ public final class ContactsContract { */ public static final Uri CONTENT_SUMMARY_FILTER_URI = Uri.withAppendedPath( CONTENT_SUMMARY_URI, "filter"); + /** + * The content:// style URI for this table joined with useful data from + * {@link Data} and {@link Presence}, filtered to include only starred aggregates + * and the most frequently contacted aggregates. + */ + public static final Uri CONTENT_SUMMARY_STREQUENT_URI = Uri.withAppendedPath( + CONTENT_SUMMARY_URI, "strequent"); + /** + * The content:// style URI used for "type-to-filter" functionality on the + * {@link CONTENT_SUMMARY_STREQUENT_URI} URI. The filter string will be used to match + * various parts of the aggregate name. The filter argument should be passed + * as an additional path segment after this URI. + */ + public static final Uri CONTENT_SUMMARY_STREQUENT_FILTER_URI = Uri.withAppendedPath( + CONTENT_SUMMARY_STREQUENT_URI, "filter"); /** * The MIME type of {@link #CONTENT_URI} providing a directory of @@ -266,7 +282,8 @@ public final class ContactsContract { * * @hide */ - public static final Uri CONTENT_FILTER_EMAIL_URI = Uri.withAppendedPath(CONTENT_URI, "filter_email"); + public static final Uri CONTENT_FILTER_EMAIL_URI = + Uri.withAppendedPath(CONTENT_URI, "filter_email"); /** * The MIME type of {@link #CONTENT_URI} providing a directory of @@ -948,7 +965,8 @@ public final class ContactsContract { /** * Constants for the contact aggregation exceptions table, which contains - * aggregation rules overriding those used by automatic aggregation. + * aggregation rules overriding those used by automatic aggregation. This type only + * supports query and update. Neither insert nor delete are supported. */ public static final class AggregationExceptions implements BaseColumns { /** @@ -974,26 +992,39 @@ public final class ContactsContract { "vnd.android.cursor.item/aggregation_exception"; /** - * The type of exception: {@link #TYPE_NEVER_MATCH} or {@link #TYPE_ALWAYS_MATCH}. + * The type of exception: {@link #TYPE_KEEP_IN}, {@link #TYPE_KEEP_OUT} or + * {@link #TYPE_AUTOMATIC}. * * <P>Type: INTEGER</P> */ public static final String TYPE = "type"; - public static final int TYPE_NEVER_MATCH = 0; - public static final int TYPE_ALWAYS_MATCH = 1; + /** + * Allows the provider to automatically decide whether the aggregate should include + * a particular contact or not. + */ + public static final int TYPE_AUTOMATIC = 0; + + /** + * Makes sure that the specified contact is included in the specified aggregate. + */ + public static final int TYPE_KEEP_IN = 1; + + /** + * Makes sure that the specified contact is NOT included in the specified aggregate. + */ + public static final int TYPE_KEEP_OUT = 2; /** - * A reference to the {@link android.provider.ContactsContract.Contacts#_ID} of one of - * the contacts that the rule applies to. + * A reference to the {@link Aggregates#_ID} of the aggregate that the rule applies to. */ - public static final String CONTACT_ID1 = "contact_id1"; + public static final String AGGREGATE_ID = "aggregate_id"; /** - * A reference to the {@link android.provider.ContactsContract.Contacts#_ID} of the other + * A reference to the {@link android.provider.ContactsContract.Contacts#_ID} of the * contact that the rule applies to. */ - public static final String CONTACT_ID2 = "contact_id2"; + public static final String CONTACT_ID = "contact_id"; } private interface RestrictionExceptionsColumns { @@ -1042,4 +1073,344 @@ public final class ContactsContract { public static final Uri CONTENT_URI = Uri.withAppendedPath(AUTHORITY_URI, "restriction_exceptions"); } + + /** + * Contains helper classes used to create or manage {@link android.content.Intent Intents} + * that involve contacts. + */ + public static final class Intents { + /** + * This is the intent that is fired when a search suggestion is clicked on. + */ + public static final String SEARCH_SUGGESTION_CLICKED = + "android.provider.Contacts.SEARCH_SUGGESTION_CLICKED"; + + /** + * This is the intent that is fired when a search suggestion for dialing a number + * is clicked on. + */ + public static final String SEARCH_SUGGESTION_DIAL_NUMBER_CLICKED = + "android.provider.Contacts.SEARCH_SUGGESTION_DIAL_NUMBER_CLICKED"; + + /** + * This is the intent that is fired when a search suggestion for creating a contact + * is clicked on. + */ + public static final String SEARCH_SUGGESTION_CREATE_CONTACT_CLICKED = + "android.provider.Contacts.SEARCH_SUGGESTION_CREATE_CONTACT_CLICKED"; + + /** + * Starts an Activity that lets the user pick a contact to attach an image to. + * After picking the contact it launches the image cropper in face detection mode. + */ + public static final String ATTACH_IMAGE = + "com.android.contacts.action.ATTACH_IMAGE"; + + /** + * Takes as input a data URI with a mailto: or tel: scheme. If a single + * contact exists with the given data it will be shown. If no contact + * exists, a dialog will ask the user if they want to create a new + * contact with the provided details filled in. If multiple contacts + * share the data the user will be prompted to pick which contact they + * want to view. + * <p> + * For <code>mailto:</code> URIs, the scheme specific portion must be a + * raw email address, such as one built using + * {@link Uri#fromParts(String, String, String)}. + * <p> + * For <code>tel:</code> URIs, the scheme specific portion is compared + * to existing numbers using the standard caller ID lookup algorithm. + * The number must be properly encoded, for example using + * {@link Uri#fromParts(String, String, String)}. + * <p> + * Any extras from the {@link Insert} class will be passed along to the + * create activity if there are no contacts to show. + * <p> + * Passing true for the {@link #EXTRA_FORCE_CREATE} extra will skip + * prompting the user when the contact doesn't exist. + */ + public static final String SHOW_OR_CREATE_CONTACT = + "com.android.contacts.action.SHOW_OR_CREATE_CONTACT"; + + /** + * Used with {@link #SHOW_OR_CREATE_CONTACT} to force creating a new + * contact if no matching contact found. Otherwise, default behavior is + * to prompt user with dialog before creating. + * <p> + * Type: BOOLEAN + */ + public static final String EXTRA_FORCE_CREATE = + "com.android.contacts.action.FORCE_CREATE"; + + /** + * Used with {@link #SHOW_OR_CREATE_CONTACT} to specify an exact + * description to be shown when prompting user about creating a new + * contact. + * <p> + * Type: STRING + */ + public static final String EXTRA_CREATE_DESCRIPTION = + "com.android.contacts.action.CREATE_DESCRIPTION"; + + /** + * Intents related to the Contacts app UI. + */ + public static final class UI { + /** + * The action for the default contacts list tab. + */ + public static final String LIST_DEFAULT = + "com.android.contacts.action.LIST_DEFAULT"; + + /** + * The action for the contacts list tab. + */ + public static final String LIST_GROUP_ACTION = + "com.android.contacts.action.LIST_GROUP"; + + /** + * When in LIST_GROUP_ACTION mode, this is the group to display. + */ + public static final String GROUP_NAME_EXTRA_KEY = "com.android.contacts.extra.GROUP"; + + /** + * The action for the all contacts list tab. + */ + public static final String LIST_ALL_CONTACTS_ACTION = + "com.android.contacts.action.LIST_ALL_CONTACTS"; + + /** + * The action for the contacts with phone numbers list tab. + */ + public static final String LIST_CONTACTS_WITH_PHONES_ACTION = + "com.android.contacts.action.LIST_CONTACTS_WITH_PHONES"; + + /** + * The action for the starred contacts list tab. + */ + public static final String LIST_STARRED_ACTION = + "com.android.contacts.action.LIST_STARRED"; + + /** + * The action for the frequent contacts list tab. + */ + public static final String LIST_FREQUENT_ACTION = + "com.android.contacts.action.LIST_FREQUENT"; + + /** + * The action for the "strequent" contacts list tab. It first lists the starred + * contacts in alphabetical order and then the frequent contacts in descending + * order of the number of times they have been contacted. + */ + public static final String LIST_STREQUENT_ACTION = + "com.android.contacts.action.LIST_STREQUENT"; + + /** + * A key for to be used as an intent extra to set the activity + * title to a custom String value. + */ + public static final String TITLE_EXTRA_KEY = + "com.android.contacts.extra.TITLE_EXTRA"; + + /** + * Activity Action: Display a filtered list of contacts + * <p> + * Input: Extra field {@link #FILTER_TEXT_EXTRA_KEY} is the text to use for + * filtering + * <p> + * Output: Nothing. + */ + public static final String FILTER_CONTACTS_ACTION = + "com.android.contacts.action.FILTER_CONTACTS"; + + /** + * Used as an int extra field in {@link #FILTER_CONTACTS_ACTION} + * intents to supply the text on which to filter. + */ + public static final String FILTER_TEXT_EXTRA_KEY = + "com.android.contacts.extra.FILTER_TEXT"; + } + + /** + * Convenience class that contains string constants used + * to create contact {@link android.content.Intent Intents}. + */ + public static final class Insert { + /** The action code to use when adding a contact */ + public static final String ACTION = Intent.ACTION_INSERT; + + /** + * If present, forces a bypass of quick insert mode. + */ + public static final String FULL_MODE = "full_mode"; + + /** + * The extra field for the contact name. + * <P>Type: String</P> + */ + public static final String NAME = "name"; + + // TODO add structured name values here. + + /** + * The extra field for the contact phonetic name. + * <P>Type: String</P> + */ + public static final String PHONETIC_NAME = "phonetic_name"; + + /** + * The extra field for the contact company. + * <P>Type: String</P> + */ + public static final String COMPANY = "company"; + + /** + * The extra field for the contact job title. + * <P>Type: String</P> + */ + public static final String JOB_TITLE = "job_title"; + + /** + * The extra field for the contact notes. + * <P>Type: String</P> + */ + public static final String NOTES = "notes"; + + /** + * The extra field for the contact phone number. + * <P>Type: String</P> + */ + public static final String PHONE = "phone"; + + /** + * The extra field for the contact phone number type. + * <P>Type: Either an integer value from + * {@link android.provider.Contacts.PhonesColumns PhonesColumns}, + * or a string specifying a custom label.</P> + */ + public static final String PHONE_TYPE = "phone_type"; + + /** + * The extra field for the phone isprimary flag. + * <P>Type: boolean</P> + */ + public static final String PHONE_ISPRIMARY = "phone_isprimary"; + + /** + * The extra field for an optional second contact phone number. + * <P>Type: String</P> + */ + public static final String SECONDARY_PHONE = "secondary_phone"; + + /** + * The extra field for an optional second contact phone number type. + * <P>Type: Either an integer value from + * {@link android.provider.Contacts.PhonesColumns PhonesColumns}, + * or a string specifying a custom label.</P> + */ + public static final String SECONDARY_PHONE_TYPE = "secondary_phone_type"; + + /** + * The extra field for an optional third contact phone number. + * <P>Type: String</P> + */ + public static final String TERTIARY_PHONE = "tertiary_phone"; + + /** + * The extra field for an optional third contact phone number type. + * <P>Type: Either an integer value from + * {@link android.provider.Contacts.PhonesColumns PhonesColumns}, + * or a string specifying a custom label.</P> + */ + public static final String TERTIARY_PHONE_TYPE = "tertiary_phone_type"; + + /** + * The extra field for the contact email address. + * <P>Type: String</P> + */ + public static final String EMAIL = "email"; + + /** + * The extra field for the contact email type. + * <P>Type: Either an integer value from + * {@link android.provider.Contacts.ContactMethodsColumns ContactMethodsColumns} + * or a string specifying a custom label.</P> + */ + public static final String EMAIL_TYPE = "email_type"; + + /** + * The extra field for the email isprimary flag. + * <P>Type: boolean</P> + */ + public static final String EMAIL_ISPRIMARY = "email_isprimary"; + + /** + * The extra field for an optional second contact email address. + * <P>Type: String</P> + */ + public static final String SECONDARY_EMAIL = "secondary_email"; + + /** + * The extra field for an optional second contact email type. + * <P>Type: Either an integer value from + * {@link android.provider.Contacts.ContactMethodsColumns ContactMethodsColumns} + * or a string specifying a custom label.</P> + */ + public static final String SECONDARY_EMAIL_TYPE = "secondary_email_type"; + + /** + * The extra field for an optional third contact email address. + * <P>Type: String</P> + */ + public static final String TERTIARY_EMAIL = "tertiary_email"; + + /** + * The extra field for an optional third contact email type. + * <P>Type: Either an integer value from + * {@link android.provider.Contacts.ContactMethodsColumns ContactMethodsColumns} + * or a string specifying a custom label.</P> + */ + public static final String TERTIARY_EMAIL_TYPE = "tertiary_email_type"; + + /** + * The extra field for the contact postal address. + * <P>Type: String</P> + */ + public static final String POSTAL = "postal"; + + /** + * The extra field for the contact postal address type. + * <P>Type: Either an integer value from + * {@link android.provider.Contacts.ContactMethodsColumns ContactMethodsColumns} + * or a string specifying a custom label.</P> + */ + public static final String POSTAL_TYPE = "postal_type"; + + /** + * The extra field for the postal isprimary flag. + * <P>Type: boolean</P> + */ + public static final String POSTAL_ISPRIMARY = "postal_isprimary"; + + /** + * The extra field for an IM handle. + * <P>Type: String</P> + */ + public static final String IM_HANDLE = "im_handle"; + + /** + * The extra field for the IM protocol + * <P>Type: the result of {@link Contacts.ContactMethods#encodePredefinedImProtocol} + * or {@link Contacts.ContactMethods#encodeCustomImProtocol}.</P> + */ + public static final String IM_PROTOCOL = "im_protocol"; + + /** + * The extra field for the IM isprimary flag. + * <P>Type: boolean</P> + */ + public static final String IM_ISPRIMARY = "im_isprimary"; + } + } + } diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 03c7f28..5aa2174 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -1976,6 +1976,16 @@ public final class Settings { public static final String TTS_DEFAULT_LANG = "tts_default_lang"; /** + * Default text-to-speech country. + */ + public static final String TTS_DEFAULT_COUNTRY = "tts_default_country"; + + /** + * Default text-to-speech locale variant. + */ + public static final String TTS_DEFAULT_VARIANT = "tts_default_variant"; + + /** * Whether to notify the user of open networks. * <p> * If not connected and the scan results have an open network, we will @@ -2180,6 +2190,14 @@ public final class Settings { public static final String TTY_MODE_ENABLED = "tty_mode_enabled"; /** + * Flag for allowing service provider to use location information to improve products and + * services. + * Type: int ( 0 = disallow, 1 = allow ) + * @hide + */ + public static final String USE_LOCATION_FOR_SERVICES = "use_location"; + + /** * Helper method for determining if a location provider is enabled. * @param cr the content resolver to use * @param provider the location provider to query @@ -2623,26 +2641,6 @@ public final class Settings { "gtalk_wifi_max_heartbeat_ping_interval_ms"; /** - * The minimum interval for how frequently we send heartbeat pings to the GTalk server. - */ - public static final String GTALK_SERVICE_MIN_HEARTBEAT_INTERVAL_MS = - "gtalk_min_heartbeat_ping_interval_ms"; - - /** - * The scale down factor used by adaptive heartbeat logic (to scale down the heartbeat - * interval) when the previous interval fails to get a response from the server. - */ - public static final String GTALK_SERVICE_ADAPTIVE_HEARTBEAT_SCALER = - "gtalk_adaptive_heartbeat_scaler"; - - /** - * The trigger for adaptively scaling down the heartbeat interval. This is the number of - * consecutive times we failed to get a server response for sending the heartbeat ping. - */ - public static final String GTALK_SERVICE_ADAPTIVE_HEARTBEAT_TRIGGER = - "gtalk_adaptive_heartbeat_trigger"; - - /** * How long we wait to receive a heartbeat ping acknowledgement (or another packet) * from the GTalk server, before deeming the connection dead. */ @@ -2695,6 +2693,15 @@ public final class Settings { public static final String GTALK_USE_BARE_JID_TIMEOUT_MS = "gtalk_use_barejid_timeout_ms"; /** + * This is the threshold of retry number when there is an authentication expired failure + * for Google Talk. In some situation, e.g. when a Google Apps account is disabled chat + * service, the connection keeps failing. This threshold controls when we should stop + * the retrying. + */ + public static final String GTALK_MAX_RETRIES_FOR_AUTH_EXPIRED = + "gtalk_max_retries_for_auth_expired"; + + /** * Enable use of ssl session caching. * 'db' - save each session in a (per process) database * 'file' - save each session in a (per process) file @@ -2791,6 +2798,13 @@ public final class Settings { public static final String VENDING_TAB_2_TITLE = "vending_tab_2_title"; /** + * Frequency in milliseconds at which we should request MCS heartbeats + * from the Vending Machine client. + */ + public static final String VENDING_HEARTBEAT_FREQUENCY_MS = + "vending_heartbeat_frequency_ms"; + + /** * URL that points to the legal terms of service to display in Settings. * <p> * This should be a https URL. For a pretty user-friendly URL, use @@ -3094,6 +3108,7 @@ public final class Settings { * Flag for allowing service provider to use location information to improve products and * services. * Type: int ( 0 = disallow, 1 = allow ) + * @deprecated */ public static final String USE_LOCATION_FOR_SERVICES = "use_location"; diff --git a/core/java/android/server/BluetoothDeviceService.java b/core/java/android/server/BluetoothDeviceService.java index 36c432b..dcaede2 100644 --- a/core/java/android/server/BluetoothDeviceService.java +++ b/core/java/android/server/BluetoothDeviceService.java @@ -1039,6 +1039,8 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub { break; } pw.println("getHeadsetAddress() = " + headset.getHeadsetAddress()); + pw.println("getBatteryUsageHint() = " + headset.getBatteryUsageHint()); + headset.close(); } diff --git a/core/java/android/speech/tts/ITts.aidl b/core/java/android/speech/tts/ITts.aidl index 739a8e4..02211fd 100755 --- a/core/java/android/speech/tts/ITts.aidl +++ b/core/java/android/speech/tts/ITts.aidl @@ -39,7 +39,7 @@ interface ITts { void addSpeechFile(in String text, in String filename);
- void setLanguage(in String language);
+ void setLanguage(in String language, in String country, in String variant);
boolean synthesizeToFile(in String text, in String[] params, in String outputDirectory);
diff --git a/core/java/android/speech/tts/TextToSpeech.java b/core/java/android/speech/tts/TextToSpeech.java index 2c0c09e..9fc143d 100755 --- a/core/java/android/speech/tts/TextToSpeech.java +++ b/core/java/android/speech/tts/TextToSpeech.java @@ -22,13 +22,12 @@ import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; -import android.content.pm.PackageManager; -import android.content.pm.ResolveInfo; import android.os.IBinder; import android.os.RemoteException; import android.util.Log; import java.util.HashMap; +import java.util.Locale; /** * @@ -36,7 +35,7 @@ import java.util.HashMap; * * {@hide} */ -//TODO #TTS# review + complete javadoc +//TODO #TTS# review + complete javadoc + add links to constants public class TextToSpeech { /** @@ -52,9 +51,18 @@ public class TextToSpeech { */ public static final int TTS_ERROR_MISSING_RESOURCE = -2; + /** + * Queue mode where all entries in the playback queue (media to be played + * and text to be synthesized) are dropped and replaced by the new entry. + */ + public static final int TTS_QUEUE_FLUSH = 0; + /** + * Queue mode where the new entry is added at the end of the playback queue. + */ + public static final int TTS_QUEUE_ADD = 1; /** - * Called when the TTS has initialized + * Called when the TTS has initialized. * * The InitListener must implement the onInit function. onInit is passed a * status code indicating the result of the TTS initialization. @@ -73,9 +81,31 @@ public class TextToSpeech { } /** - * Connection needed for the TTS + * Internal constants for the TTS functionality + * + * {@hide} + */ + public class Engine { + // default values for a TTS engine when settings are not found in the provider + public static final int FALLBACK_TTS_DEFAULT_RATE = 100; // 1x + public static final int FALLBACK_TTS_DEFAULT_PITCH = 100;// 1x + public static final int FALLBACK_TTS_USE_DEFAULTS = 0; // false + public static final String FALLBACK_TTS_DEFAULT_LANG = "eng"; + public static final String FALLBACK_TTS_DEFAULT_COUNTRY = ""; + public static final String FALLBACK_TTS_DEFAULT_VARIANT = ""; + + // return codes for a TTS engine's check data activity + public static final int CHECK_VOICE_DATA_PASS = 1; + public static final int CHECK_VOICE_DATA_FAIL = 0; + public static final int CHECK_VOICE_DATA_BAD_DATA = -1; + public static final int CHECK_VOICE_DATA_MISSING_DATA = -2; + public static final int CHECK_VOICE_DATA_MISSING_DATA_NO_SDCARD = -3; + } + + /** + * Connection needed for the TTS. */ - private ServiceConnection serviceConnection; + private ServiceConnection mServiceConnection; private ITts mITts = null; private Context mContext = null; @@ -104,8 +134,7 @@ public class TextToSpeech { } - public void setOnSpeechCompletedListener( - final OnSpeechCompletedListener listener) { + public void setOnSpeechCompletedListener(final OnSpeechCompletedListener listener) { synchronized(mSpeechCompListenerLock) { mSpeechCompListener = listener; } @@ -126,7 +155,7 @@ public class TextToSpeech { mStarted = false; // Initialize the TTS, run the callback after the binding is successful - serviceConnection = new ServiceConnection() { + mServiceConnection = new ServiceConnection() { public void onServiceConnected(ComponentName name, IBinder service) { synchronized(mStartLock) { mITts = ITts.Stub.asInterface(service); @@ -176,7 +205,7 @@ public class TextToSpeech { Intent intent = new Intent("android.intent.action.USE_TTS"); intent.addCategory("android.intent.category.TTS"); - mContext.bindService(intent, serviceConnection, + mContext.bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE); // TODO handle case where the binding works (should always work) but // the plugin fails @@ -190,7 +219,7 @@ public class TextToSpeech { */ public void shutdown() { try { - mContext.unbindService(serviceConnection); + mContext.unbindService(mServiceConnection); } catch (IllegalArgumentException e) { // Do nothing and fail silently since an error here indicates that // binding never succeeded in the first place. @@ -291,8 +320,8 @@ public class TextToSpeech { * @param text * The string of text to be spoken. * @param queueMode - * The queuing strategy to use. Use 0 for no queuing, and 1 for - * queuing. + * The queuing strategy to use. + * See TTS_QUEUE_ADD and TTS_QUEUE_FLUSH. * @param params * The hashmap of speech parameters to be used. */ @@ -329,12 +358,11 @@ public class TextToSpeech { * @param earcon * The earcon that should be played * @param queueMode - * 0 for no queue (interrupts all previous utterances), 1 for - * queued + * See TTS_QUEUE_ADD and TTS_QUEUE_FLUSH. * @param params * The hashmap of parameters to be used. */ - public void playEarcon(String earcon, int queueMode, + public void playEarcon(String earcon, int queueMode, HashMap<String,String> params) { synchronized (mStartLock) { if (!mStarted) { @@ -358,8 +386,8 @@ public class TextToSpeech { } } } - - + + public void playSilence(long durationInMs, int queueMode) { // TODO implement, already present in TTS service } @@ -429,20 +457,22 @@ public class TextToSpeech { * Note that the speech rate is not universally supported by all engines and * will be treated as a hint. The TTS library will try to use the specified * speech rate, but there is no guarantee. - * - * Currently, this will change the speech rate for the espeak engine, but it - * has no effect on any pre-recorded speech. + * This has no effect on any pre-recorded speech. * * @param speechRate - * The speech rate for the TTS engine. + * The speech rate for the TTS engine. 1 is the normal speed, + * lower values slow down the speech (0.5 is half the normal speech rate), + * greater values accelerate it (2 is twice the normal speech rate). */ - public void setSpeechRate(int speechRate) { + public void setSpeechRate(float speechRate) { synchronized (mStartLock) { if (!mStarted) { return; } try { - mITts.setSpeechRate(speechRate); + if (speechRate > 0) { + mITts.setSpeechRate((int)(speechRate*100)); + } } catch (RemoteException e) { // TTS died; restart it. mStarted = false; @@ -457,24 +487,18 @@ public class TextToSpeech { * * Note that the language is not universally supported by all engines and * will be treated as a hint. The TTS library will try to use the specified - * language, but there is no guarantee. - * - * Currently, this will change the language for the espeak engine, but it - * has no effect on any pre-recorded speech. + * language as represented by the Locale, but there is no guarantee. * - * @param language - * The language to be used. The languages are specified by their - * IETF language tags as defined by BCP 47. This is the same - * standard used for the lang attribute in HTML. See: - * http://en.wikipedia.org/wiki/IETF_language_tag + * @param loc + * The locale describing the language to be used. */ - public void setLanguage(String language) { + public void setLanguage(Locale loc) { synchronized (mStartLock) { if (!mStarted) { return; } try { - mITts.setLanguage(language); + mITts.setLanguage(loc.getISO3Language(), loc.getISO3Country(), loc.getVariant()); } catch (RemoteException e) { // TTS died; restart it. mStarted = false; diff --git a/core/java/android/test/InstrumentationTestCase.java b/core/java/android/test/InstrumentationTestCase.java index 470ab0d..2145d7c 100644 --- a/core/java/android/test/InstrumentationTestCase.java +++ b/core/java/android/test/InstrumentationTestCase.java @@ -241,7 +241,13 @@ public class InstrumentationTestCase extends TestCase { try { final Field keyCodeField = KeyEvent.class.getField("KEYCODE_" + key); final int keyCode = keyCodeField.getInt(null); - instrumentation.sendKeyDownUpSync(keyCode); + try { + instrumentation.sendKeyDownUpSync(keyCode); + } catch (SecurityException e) { + // Ignore security exceptions that are now thrown + // when trying to send to another app, to retain + // compatibility with existing tests. + } } catch (NoSuchFieldException e) { Log.w("ActivityTestCase", "Unknown keycode: KEYCODE_" + key); break; @@ -266,7 +272,13 @@ public class InstrumentationTestCase extends TestCase { final Instrumentation instrumentation = getInstrumentation(); for (int i = 0; i < count; i++) { - instrumentation.sendKeyDownUpSync(keys[i]); + try { + instrumentation.sendKeyDownUpSync(keys[i]); + } catch (SecurityException e) { + // Ignore security exceptions that are now thrown + // when trying to send to another app, to retain + // compatibility with existing tests. + } } instrumentation.waitForIdleSync(); @@ -292,7 +304,13 @@ public class InstrumentationTestCase extends TestCase { final int keyCount = keys[i]; final int keyCode = keys[i + 1]; for (int j = 0; j < keyCount; j++) { - instrumentation.sendKeyDownUpSync(keyCode); + try { + instrumentation.sendKeyDownUpSync(keyCode); + } catch (SecurityException e) { + // Ignore security exceptions that are now thrown + // when trying to send to another app, to retain + // compatibility with existing tests. + } } } diff --git a/core/java/android/text/format/DateUtils.java b/core/java/android/text/format/DateUtils.java index bccb3a6..1a4eb69 100644 --- a/core/java/android/text/format/DateUtils.java +++ b/core/java/android/text/format/DateUtils.java @@ -184,6 +184,9 @@ public class DateUtils */ public static final String HOUR_MINUTE_24 = "%H:%M"; public static final String MONTH_FORMAT = "%B"; + /** + * This is not actually a useful month name in all locales. + */ public static final String ABBREV_MONTH_FORMAT = "%b"; public static final String NUMERIC_MONTH_FORMAT = "%m"; public static final String MONTH_DAY_FORMAT = "%-d"; @@ -1444,7 +1447,8 @@ public class DateUtils if (numericDate) { monthFormat = NUMERIC_MONTH_FORMAT; } else if (abbrevMonth) { - monthFormat = ABBREV_MONTH_FORMAT; + monthFormat = + res.getString(com.android.internal.R.string.short_format_month); } else { monthFormat = MONTH_FORMAT; } diff --git a/core/java/android/util/DisplayMetrics.java b/core/java/android/util/DisplayMetrics.java index 245148d..a095913 100644 --- a/core/java/android/util/DisplayMetrics.java +++ b/core/java/android/util/DisplayMetrics.java @@ -37,8 +37,7 @@ public class DisplayMetrics { * The device's density. * @hide */ - public static final int DEVICE_DENSITY = SystemProperties.getInt("ro.sf.lcd_density", - DEFAULT_DENSITY); + public static final int DEVICE_DENSITY = getDeviceDensity(); /** * The absolute width of the display in pixels. @@ -161,4 +160,13 @@ public class DisplayMetrics { ", height=" + heightPixels + ", scaledDensity=" + scaledDensity + ", xdpi=" + xdpi + ", ydpi=" + ydpi + "}"; } + + private static int getDeviceDensity() { + // qemu.sf.lcd_density can be used to override ro.sf.lcd_density + // when running in the emulator, allowing for dynamic configurations. + // The reason for this is that ro.sf.lcd_density is write-once and is + // set by the init process when it parses build.prop before anything else. + return SystemProperties.getInt("qemu.sf.lcd_density", + SystemProperties.getInt("ro.sf.lcd_density", DEFAULT_DENSITY)); + } } diff --git a/core/java/android/util/LongSparseArray.java b/core/java/android/util/LongSparseArray.java new file mode 100644 index 0000000..d90045f --- /dev/null +++ b/core/java/android/util/LongSparseArray.java @@ -0,0 +1,342 @@ +/* + * 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.util; + +import com.android.internal.util.ArrayUtils; + +/** + * SparseArrays map longs to Objects. Unlike a normal array of Objects, + * there can be gaps in the indices. It is intended to be more efficient + * than using a HashMap to map Longs to Objects. + * + * @hide + */ +public class LongSparseArray<E> { + private static final Object DELETED = new Object(); + private boolean mGarbage = false; + + /** + * Creates a new SparseArray containing no mappings. + */ + public LongSparseArray() { + this(10); + } + + /** + * Creates a new SparseArray containing no mappings that will not + * require any additional memory allocation to store the specified + * number of mappings. + */ + public LongSparseArray(int initialCapacity) { + initialCapacity = ArrayUtils.idealIntArraySize(initialCapacity); + + mKeys = new long[initialCapacity]; + mValues = new Object[initialCapacity]; + mSize = 0; + } + + /** + * Gets the Object mapped from the specified key, or <code>null</code> + * if no such mapping has been made. + */ + public E get(long key) { + return get(key, null); + } + + /** + * Gets the Object mapped from the specified key, or the specified Object + * if no such mapping has been made. + */ + public E get(long key, E valueIfKeyNotFound) { + int i = binarySearch(mKeys, 0, mSize, key); + + if (i < 0 || mValues[i] == DELETED) { + return valueIfKeyNotFound; + } else { + return (E) mValues[i]; + } + } + + /** + * Removes the mapping from the specified key, if there was any. + */ + public void delete(long key) { + int i = binarySearch(mKeys, 0, mSize, key); + + if (i >= 0) { + if (mValues[i] != DELETED) { + mValues[i] = DELETED; + mGarbage = true; + } + } + } + + /** + * Alias for {@link #delete(long)}. + */ + public void remove(long key) { + delete(key); + } + + private void gc() { + // Log.e("SparseArray", "gc start with " + mSize); + + int n = mSize; + int o = 0; + long[] keys = mKeys; + Object[] values = mValues; + + for (int i = 0; i < n; i++) { + Object val = values[i]; + + if (val != DELETED) { + if (i != o) { + keys[o] = keys[i]; + values[o] = val; + } + + o++; + } + } + + mGarbage = false; + mSize = o; + + // Log.e("SparseArray", "gc end with " + mSize); + } + + /** + * Adds a mapping from the specified key to the specified value, + * replacing the previous mapping from the specified key if there + * was one. + */ + public void put(long key, E value) { + int i = binarySearch(mKeys, 0, mSize, key); + + if (i >= 0) { + mValues[i] = value; + } else { + i = ~i; + + if (i < mSize && mValues[i] == DELETED) { + mKeys[i] = key; + mValues[i] = value; + return; + } + + if (mGarbage && mSize >= mKeys.length) { + gc(); + + // Search again because indices may have changed. + i = ~binarySearch(mKeys, 0, mSize, key); + } + + if (mSize >= mKeys.length) { + int n = ArrayUtils.idealIntArraySize(mSize + 1); + + long[] nkeys = new long[n]; + Object[] nvalues = new Object[n]; + + // Log.e("SparseArray", "grow " + mKeys.length + " to " + n); + System.arraycopy(mKeys, 0, nkeys, 0, mKeys.length); + System.arraycopy(mValues, 0, nvalues, 0, mValues.length); + + mKeys = nkeys; + mValues = nvalues; + } + + if (mSize - i != 0) { + // Log.e("SparseArray", "move " + (mSize - i)); + System.arraycopy(mKeys, i, mKeys, i + 1, mSize - i); + System.arraycopy(mValues, i, mValues, i + 1, mSize - i); + } + + mKeys[i] = key; + mValues[i] = value; + mSize++; + } + } + + /** + * Returns the number of key-value mappings that this SparseArray + * currently stores. + */ + public int size() { + if (mGarbage) { + gc(); + } + + return mSize; + } + + /** + * Given an index in the range <code>0...size()-1</code>, returns + * the key from the <code>index</code>th key-value mapping that this + * SparseArray stores. + */ + public long keyAt(int index) { + if (mGarbage) { + gc(); + } + + return mKeys[index]; + } + + /** + * Given an index in the range <code>0...size()-1</code>, returns + * the value from the <code>index</code>th key-value mapping that this + * SparseArray stores. + */ + public E valueAt(int index) { + if (mGarbage) { + gc(); + } + + return (E) mValues[index]; + } + + /** + * Given an index in the range <code>0...size()-1</code>, sets a new + * value for the <code>index</code>th key-value mapping that this + * SparseArray stores. + */ + public void setValueAt(int index, E value) { + if (mGarbage) { + gc(); + } + + mValues[index] = value; + } + + /** + * Returns the index for which {@link #keyAt} would return the + * specified key, or a negative number if the specified + * key is not mapped. + */ + public int indexOfKey(long key) { + if (mGarbage) { + gc(); + } + + return binarySearch(mKeys, 0, mSize, key); + } + + /** + * Returns an index for which {@link #valueAt} would return the + * specified key, or a negative number if no keys map to the + * specified value. + * Beware that this is a linear search, unlike lookups by key, + * and that multiple keys can map to the same value and this will + * find only one of them. + */ + public int indexOfValue(E value) { + if (mGarbage) { + gc(); + } + + for (int i = 0; i < mSize; i++) + if (mValues[i] == value) + return i; + + return -1; + } + + /** + * Removes all key-value mappings from this SparseArray. + */ + public void clear() { + int n = mSize; + Object[] values = mValues; + + for (int i = 0; i < n; i++) { + values[i] = null; + } + + mSize = 0; + mGarbage = false; + } + + /** + * Puts a key/value pair into the array, optimizing for the case where + * the key is greater than all existing keys in the array. + */ + public void append(long key, E value) { + if (mSize != 0 && key <= mKeys[mSize - 1]) { + put(key, value); + return; + } + + if (mGarbage && mSize >= mKeys.length) { + gc(); + } + + int pos = mSize; + if (pos >= mKeys.length) { + int n = ArrayUtils.idealIntArraySize(pos + 1); + + long[] nkeys = new long[n]; + Object[] nvalues = new Object[n]; + + // Log.e("SparseArray", "grow " + mKeys.length + " to " + n); + System.arraycopy(mKeys, 0, nkeys, 0, mKeys.length); + System.arraycopy(mValues, 0, nvalues, 0, mValues.length); + + mKeys = nkeys; + mValues = nvalues; + } + + mKeys[pos] = key; + mValues[pos] = value; + mSize = pos + 1; + } + + private static int binarySearch(long[] a, int start, int len, long key) { + int high = start + len, low = start - 1, guess; + + while (high - low > 1) { + guess = (high + low) / 2; + + if (a[guess] < key) + low = guess; + else + high = guess; + } + + if (high == start + len) + return ~(start + len); + else if (a[high] == key) + return high; + else + return ~high; + } + + private void checkIntegrity() { + for (int i = 1; i < mSize; i++) { + if (mKeys[i] <= mKeys[i - 1]) { + for (int j = 0; j < mSize; j++) { + Log.e("FAIL", j + ": " + mKeys[j] + " -> " + mValues[j]); + } + + throw new RuntimeException(); + } + } + } + + private long[] mKeys; + private Object[] mValues; + private int mSize; +}
\ No newline at end of file diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 0497344..ec8fd96 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -7157,8 +7157,10 @@ public class View implements Drawable.Callback, KeyEvent.Callback, Accessibility getLocationInWindow(location); final AttachInfo info = mAttachInfo; - location[0] += info.mWindowLeft; - location[1] += info.mWindowTop; + if (info != null) { + location[0] += info.mWindowLeft; + location[1] += info.mWindowTop; + } } /** diff --git a/core/java/android/webkit/ByteArrayBuilder.java b/core/java/android/webkit/ByteArrayBuilder.java index 806b458..145411c 100644 --- a/core/java/android/webkit/ByteArrayBuilder.java +++ b/core/java/android/webkit/ByteArrayBuilder.java @@ -17,6 +17,7 @@ package android.webkit; import java.util.LinkedList; +import java.util.ListIterator; /** Utility class optimized for accumulating bytes, and then spitting them back out. It does not optimize for returning the result in a @@ -94,6 +95,20 @@ class ByteArrayBuilder { return mChunks.isEmpty(); } + public int size() { + return mChunks.size(); + } + + public int getByteSize() { + int total = 0; + ListIterator<Chunk> it = mChunks.listIterator(0); + while (it.hasNext()) { + Chunk c = it.next(); + total += c.mLength; + } + return total; + } + public synchronized void clear() { Chunk c = getFirstChunk(); while (c != null) { diff --git a/core/java/android/webkit/JWebCoreJavaBridge.java b/core/java/android/webkit/JWebCoreJavaBridge.java index 507fce3..cbe337a 100644 --- a/core/java/android/webkit/JWebCoreJavaBridge.java +++ b/core/java/android/webkit/JWebCoreJavaBridge.java @@ -18,6 +18,7 @@ package android.webkit; import android.os.Handler; import android.os.Message; +import android.security.Keystore; import android.util.Log; final class JWebCoreJavaBridge extends Handler { @@ -135,10 +136,9 @@ final class JWebCoreJavaBridge extends Handler { /** * Store a cookie string associated with a url. * @param url The url to be used as a key for the cookie. - * @param docUrl The policy base url used by WebCore. * @param value The cookie string to be stored. */ - private void setCookies(String url, String docUrl, String value) { + private void setCookies(String url, String value) { if (value.contains("\r") || value.contains("\n")) { // for security reason, filter out '\r' and '\n' from the cookie int size = value.length(); @@ -223,18 +223,11 @@ final class JWebCoreJavaBridge extends Handler { } private String[] getKeyStrengthList() { - // FIXME: fake the list for now - String[] list = new String[2]; - list[0] = "1024"; - list[1] = "512"; - return list; + return Keystore.getInstance().getSupportedKeyStrenghs(); } private String getSignedPublicKey(int index, String challenge, String url) { - // FIXME: do nothing for now - Log.w(LOGTAG, "getSignedPublicKey for " + index + " and challenge=" - + challenge + " and url=" + url); - return ""; + return Keystore.getInstance().generateKeyPair(index, challenge, url); } private native void nativeConstructor(); diff --git a/core/java/android/webkit/LoadListener.java b/core/java/android/webkit/LoadListener.java index 1ebdb79..08854f7 100644 --- a/core/java/android/webkit/LoadListener.java +++ b/core/java/android/webkit/LoadListener.java @@ -25,16 +25,16 @@ import android.net.http.HttpAuthHeader; import android.net.http.RequestHandle; import android.net.http.SslCertificate; import android.net.http.SslError; -import android.net.http.SslCertificate; import android.os.Handler; import android.os.Message; +import android.security.Keystore; import android.util.Log; import android.webkit.CacheManager.CacheResult; +import android.widget.Toast; import com.android.internal.R; -import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; @@ -70,6 +70,8 @@ class LoadListener extends Handler implements EventHandler { private static final int HTTP_NOT_FOUND = 404; private static final int HTTP_PROXY_AUTH = 407; + private static final String CERT_MIMETYPE = "application/x-x509-ca-cert"; + private static int sNativeLoaderCount; private final ByteArrayBuilder mDataBuilder = new ByteArrayBuilder(8192); @@ -919,6 +921,12 @@ class LoadListener extends Handler implements EventHandler { // This commits the headers without checking the response status code. private void commitHeaders() { + if (mIsMainPageLoader && CERT_MIMETYPE.equals(mMimeType)) { + // In the case of downloading certificate, we will save it to the + // Keystore in commitLoad. Do not call webcore. + return; + } + // Commit the headers to WebCore int nativeResponse = createNativeResponse(); // The native code deletes the native response object. @@ -959,6 +967,30 @@ class LoadListener extends Handler implements EventHandler { private void commitLoad() { if (mCancelled) return; + if (mIsMainPageLoader && CERT_MIMETYPE.equals(mMimeType)) { + // In the case of downloading certificate, we will save it to the + // Keystore and stop the current loading so that it will not + // generate a new history page + byte[] cert = new byte[mDataBuilder.getByteSize()]; + int position = 0; + ByteArrayBuilder.Chunk c; + while (true) { + c = mDataBuilder.getFirstChunk(); + if (c == null) break; + + if (c.mLength != 0) { + System.arraycopy(c.mArray, 0, cert, position, c.mLength); + position += c.mLength; + } + mDataBuilder.releaseChunk(c); + } + Keystore.getInstance().addCertificate(cert); + Toast.makeText(mContext, R.string.certificateSaved, + Toast.LENGTH_SHORT).show(); + mBrowserFrame.stopLoading(); + return; + } + // Give the data to WebKit now PerfChecker checker = new PerfChecker(); ByteArrayBuilder.Chunk c; diff --git a/core/java/android/webkit/WebStorage.java b/core/java/android/webkit/WebStorage.java index f27360d..0488691 100644 --- a/core/java/android/webkit/WebStorage.java +++ b/core/java/android/webkit/WebStorage.java @@ -200,8 +200,13 @@ public final class WebStorage { */ public void setQuotaForOrigin(String origin, long quota) { if (origin != null) { - postMessage(Message.obtain(null, SET_QUOTA_ORIGIN, - new Origin(origin, quota))); + if (WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName())) { + nativeSetQuotaForOrigin(origin, quota); + syncValues(); + } else { + postMessage(Message.obtain(null, SET_QUOTA_ORIGIN, + new Origin(origin, quota))); + } } } @@ -211,8 +216,13 @@ public final class WebStorage { */ public void deleteOrigin(String origin) { if (origin != null) { - postMessage(Message.obtain(null, DELETE_ORIGIN, - new Origin(origin))); + if (WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName())) { + nativeDeleteOrigin(origin); + syncValues(); + } else { + postMessage(Message.obtain(null, DELETE_ORIGIN, + new Origin(origin))); + } } } @@ -221,7 +231,12 @@ public final class WebStorage { * Delete all databases */ public void deleteAllDatabases() { - postMessage(Message.obtain(null, DELETE_ALL)); + if (WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName())) { + nativeDeleteAllDatabases(); + syncValues(); + } else { + postMessage(Message.obtain(null, DELETE_ALL)); + } } /** @@ -250,7 +265,11 @@ public final class WebStorage { * Post a Sync request */ public void update() { - postMessage(Message.obtain(null, UPDATE)); + if (WebViewCore.THREAD_NAME.equals(Thread.currentThread().getName())) { + syncValues(); + } else { + postMessage(Message.obtain(null, UPDATE)); + } } /** diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java index 41e007e..2d8950e 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -489,22 +489,27 @@ public class WebView extends AbsoluteLayout // width which view is considered to be fully zoomed out static final int ZOOM_OUT_WIDTH = 1008; - private static final float DEFAULT_MAX_ZOOM_SCALE = 4.0f; - private static final float DEFAULT_MIN_ZOOM_SCALE = 0.25f; + // default scale limit. Depending on the display density + private static float DEFAULT_MAX_ZOOM_SCALE; + private static float DEFAULT_MIN_ZOOM_SCALE; // scale limit, which can be set through viewport meta tag in the web page - private float mMaxZoomScale = DEFAULT_MAX_ZOOM_SCALE; - private float mMinZoomScale = DEFAULT_MIN_ZOOM_SCALE; + private float mMaxZoomScale; + private float mMinZoomScale; private boolean mMinZoomScaleFixed = false; // initial scale in percent. 0 means using default. private int mInitialScale = 0; + // default scale. Depending on the display density. + static int DEFAULT_SCALE_PERCENT; + private float DEFAULT_SCALE; + // set to true temporarily while the zoom control is being dragged private boolean mPreviewZoomOnly = false; // computed scale and inverse, from mZoomWidth. - private float mActualScale = 1; - private float mInvActualScale = 1; + private float mActualScale; + private float mInvActualScale; // if this is non-zero, it is used on drawing rather than mActualScale private float mZoomScale; private float mInvInitialZoomScale; @@ -734,10 +739,19 @@ public class WebView extends AbsoluteLayout final int slop = ViewConfiguration.get(getContext()).getScaledTouchSlop(); mTouchSlopSquare = slop * slop; mMinLockSnapReverseDistance = slop; + final float density = getContext().getResources().getDisplayMetrics().density; // use one line height, 16 based on our current default font, for how // far we allow a touch be away from the edge of a link - mNavSlop = (int) (16 * getContext().getResources() - .getDisplayMetrics().density); + mNavSlop = (int) (16 * density); + // density adjusted scale factors + DEFAULT_SCALE_PERCENT = (int) (100 * density); + DEFAULT_SCALE = density; + mActualScale = density; + mInvActualScale = 1 / density; + DEFAULT_MAX_ZOOM_SCALE = 4.0f * density; + DEFAULT_MIN_ZOOM_SCALE = 0.25f * density; + mMaxZoomScale = DEFAULT_MAX_ZOOM_SCALE; + mMinZoomScale = DEFAULT_MIN_ZOOM_SCALE; } /* package */ boolean onSavePassword(String schemePlusHost, String username, @@ -1750,8 +1764,9 @@ public class WebView extends AbsoluteLayout } // Rect.equals() checks for null input. if (!rect.equals(mLastVisibleRectSent)) { + Point pos = new Point(rect.left, rect.top); mWebViewCore.sendMessage(EventHub.SET_SCROLL_OFFSET, - rect.left, rect.top); + nativeMoveGeneration(), 0, pos); mLastVisibleRectSent = rect; } Rect globalRect = new Rect(); @@ -2061,7 +2076,33 @@ public class WebView extends AbsoluteLayout * @return the address, or if no address is found, return null. */ public static String findAddress(String addr) { - return WebViewCore.nativeFindAddress(addr); + return findAddress(addr, false); + } + + /** + * @hide + * Return the first substring consisting of the address of a physical + * location. Currently, only addresses in the United States are detected, + * and consist of: + * - a house number + * - a street name + * - a street type (Road, Circle, etc), either spelled out or abbreviated + * - a city name + * - a state or territory, either spelled out or two-letter abbr. + * - an optional 5 digit or 9 digit zip code. + * + * Names are optionally capitalized, and the zip code, if present, + * must be valid for the state. The street type must be a standard USPS + * spelling or abbreviation. The state or territory must also be spelled + * or abbreviated using USPS standards. The house number may not exceed + * five digits. + * @param addr The string to search for addresses. + * @param caseInsensitive addr Set to true to make search ignore case. + * + * @return the address, or if no address is found, return null. + */ + public static String findAddress(String addr, boolean caseInsensitive) { + return WebViewCore.nativeFindAddress(addr, caseInsensitive); } /* @@ -2889,10 +2930,9 @@ public class WebView extends AbsoluteLayout WebViewCore.CursorData result = new WebViewCore.CursorData(); result.mMoveGeneration = nativeMoveGeneration(); result.mFrame = nativeCursorFramePointer(); - result.mNode = nativeCursorNodePointer(); - Rect bounds = nativeCursorNodeBounds(); - result.mX = bounds.centerX(); - result.mY = bounds.centerY(); + Point position = nativeCursorPosition(); + result.mX = position.x; + result.mY = position.y; return result; } @@ -4191,9 +4231,9 @@ public class WebView extends AbsoluteLayout private boolean zoomWithPreview(float scale) { float oldScale = mActualScale; - // snap to 100% if it is close - if (scale > 0.95f && scale < 1.05f) { - scale = 1.0f; + // snap to DEFAULT_SCALE if it is close + if (scale > (DEFAULT_SCALE - 0.05) && scale < (DEFAULT_SCALE + 0.05)) { + scale = DEFAULT_SCALE; } setNewZoomScale(scale, false); @@ -4582,6 +4622,9 @@ public class WebView extends AbsoluteLayout break; } case SWITCH_TO_CLICK: + // The user clicked with the trackball, and did not click a + // second time, so perform the action of a trackball single + // click mTouchMode = TOUCH_DONE_MODE; Rect visibleRect = sendOurVisibleRect(); // Note that sendOurVisibleRect calls viewToContent, so the @@ -4593,9 +4636,14 @@ public class WebView extends AbsoluteLayout mWebViewCore.sendMessage(EventHub.SET_MOVE_MOUSE, cursorData()); playSoundEffect(SoundEffectConstants.CLICK); - if (!mCallbackProxy.uiOverrideUrlLoading(nativeCursorText())) { + boolean isTextInput = nativeCursorIsTextInput(); + if (isTextInput || !mCallbackProxy.uiOverrideUrlLoading( + nativeCursorText())) { mWebViewCore.sendMessage(EventHub.CLICK); } + if (isTextInput) { + rebuildWebTextView(); + } break; case SCROLL_BY_MSG_ID: setContentScrollBy(msg.arg1, msg.arg2, (Boolean) msg.obj); @@ -4714,8 +4762,8 @@ public class WebView extends AbsoluteLayout } int initialScale = msg.arg1; int viewportWidth = msg.arg2; - // by default starting a new page with 100% zoom scale. - float scale = 1.0f; + // start a new page with DEFAULT_SCALE zoom scale. + float scale = DEFAULT_SCALE; if (mInitialScale > 0) { scale = mInitialScale / 100.0f; } else { @@ -5210,6 +5258,7 @@ public class WebView extends AbsoluteLayout private native boolean nativeCursorIntersects(Rect visibleRect); private native boolean nativeCursorIsAnchor(); private native boolean nativeCursorIsTextInput(); + private native Point nativeCursorPosition(); private native String nativeCursorText(); /** * Returns true if the native cursor node says it wants to handle key events diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java index 90d0709..4ad9a1a 100644 --- a/core/java/android/webkit/WebViewCore.java +++ b/core/java/android/webkit/WebViewCore.java @@ -94,7 +94,7 @@ final class WebViewCore { private boolean mViewportUserScalable = true; - private int mRestoredScale = 100; + private int mRestoredScale = WebView.DEFAULT_SCALE_PERCENT; private int mRestoredX = 0; private int mRestoredY = 0; @@ -309,7 +309,7 @@ final class WebViewCore { // JNI methods //------------------------------------------------------------------------- - static native String nativeFindAddress(String addr); + static native String nativeFindAddress(String addr, boolean caseInsensitive); /** * Empty the picture set. @@ -379,12 +379,10 @@ final class WebViewCore { private native void nativeSaveDocumentState(int frame); - private native void nativeMoveMouse(int framePtr, int nodePtr, int x, - int y); + private native void nativeMoveMouse(int framePtr, int x, int y); private native void nativeMoveMouseIfLatest(int moveGeneration, - int framePtr, int nodePtr, int x, int y, - boolean ignoreNullFocus); + int framePtr, int x, int y); private native String nativeRetrieveHref(int framePtr, int nodePtr); @@ -524,16 +522,13 @@ final class WebViewCore { CursorData() {} CursorData(int frame, int node, int x, int y) { mFrame = frame; - mNode = node; mX = x; mY = y; } int mMoveGeneration; int mFrame; - int mNode; int mX; int mY; - boolean mIgnoreNullFocus; } static class JSInterfaceData { @@ -725,7 +720,7 @@ final class WebViewCore { public void handleMessage(Message msg) { if (DebugFlags.WEB_VIEW_CORE) { Log.v(LOGTAG, msg.what < LOAD_URL || msg.what - > SET_INACTIVE ? Integer.toString(msg.what) + > FREE_MEMORY ? Integer.toString(msg.what) : HandlerDebugString[msg.what - LOAD_URL]); } switch (msg.what) { @@ -819,7 +814,8 @@ final class WebViewCore { case SET_SCROLL_OFFSET: // note: these are in document coordinates // (inv-zoom) - nativeSetScrollOffset(msg.arg1, msg.arg2); + Point pt = (Point) msg.obj; + nativeSetScrollOffset(msg.arg1, pt.x, pt.y); break; case SET_GLOBAL_BOUNDS: @@ -973,16 +969,14 @@ final class WebViewCore { case SET_MOVE_MOUSE: CursorData cursorData = (CursorData) msg.obj; nativeMoveMouse(cursorData.mFrame, - cursorData.mNode, cursorData.mX, - cursorData.mY); + cursorData.mX, cursorData.mY); break; case SET_MOVE_MOUSE_IF_LATEST: CursorData cData = (CursorData) msg.obj; nativeMoveMouseIfLatest(cData.mMoveGeneration, - cData.mFrame, cData.mNode, - cData.mX, cData.mY, - cData.mIgnoreNullFocus); + cData.mFrame, + cData.mX, cData.mY); break; case REQUEST_CURSOR_HREF: { @@ -1627,16 +1621,16 @@ final class WebViewCore { // infer the values if they are not defined. if (mViewportWidth == 0) { if (mViewportInitialScale == 0) { - mViewportInitialScale = 100; + mViewportInitialScale = WebView.DEFAULT_SCALE_PERCENT; } if (mViewportMinimumScale == 0) { - mViewportMinimumScale = 100; + mViewportMinimumScale = WebView.DEFAULT_SCALE_PERCENT; } } if (mViewportUserScalable == false) { - mViewportInitialScale = 100; - mViewportMinimumScale = 100; - mViewportMaximumScale = 100; + mViewportInitialScale = WebView.DEFAULT_SCALE_PERCENT; + mViewportMinimumScale = WebView.DEFAULT_SCALE_PERCENT; + mViewportMaximumScale = WebView.DEFAULT_SCALE_PERCENT; } if (mViewportMinimumScale > mViewportInitialScale) { if (mViewportInitialScale == 0) { @@ -1652,7 +1646,8 @@ final class WebViewCore { mViewportInitialScale = mViewportMaximumScale; } } - if (mViewportWidth < 0 && mViewportInitialScale == 100) { + if (mViewportWidth < 0 + && mViewportInitialScale == WebView.DEFAULT_SCALE_PERCENT) { mViewportWidth = 0; } @@ -1728,7 +1723,7 @@ final class WebViewCore { } // these must be in document space (i.e. not scaled/zoomed). - private native void nativeSetScrollOffset(int dx, int dy); + private native void nativeSetScrollOffset(int gen, int dx, int dy); private native void nativeSetGlobalBounds(int x, int y, int w, int h); diff --git a/core/java/android/widget/AutoCompleteTextView.java b/core/java/android/widget/AutoCompleteTextView.java index ff95203..1c66803 100644 --- a/core/java/android/widget/AutoCompleteTextView.java +++ b/core/java/android/widget/AutoCompleteTextView.java @@ -619,15 +619,19 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe mDropDownList.getAdapter().getCount() - 1)) { // When the selection is at the top, we block the key // event to prevent focus from moving. - mDropDownList.hideSelector(); - mDropDownList.requestLayout(); + clearListSelection(); mPopup.setInputMethodMode(PopupWindow.INPUT_METHOD_NEEDED); mPopup.update(); return true; + } else { + // WARNING: Please read the comment where mListSelectionHidden + // is declared + mDropDownList.mListSelectionHidden = false; } + consumed = mDropDownList.onKeyDown(keyCode, event); - if (DEBUG) Log.v(TAG, "Key down: code=" + keyCode + " list consumed=" - + consumed); + if (DEBUG) Log.v(TAG, "Key down: code=" + keyCode + " list consumed=" + consumed); + if (consumed) { // If it handled the key event, then the user is // navigating in the list, so we should put it in front. @@ -770,9 +774,12 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe * it back. */ public void clearListSelection() { - if (mDropDownList != null) { - mDropDownList.hideSelector(); - mDropDownList.requestLayout(); + final DropDownListView list = mDropDownList; + if (list != null) { + // WARNING: Please read the comment where mListSelectionHidden is declared + list.mListSelectionHidden = true; + list.hideSelector(); + list.requestLayout(); } } @@ -1062,8 +1069,7 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe mPopup.showAsDropDown(getDropDownAnchorView(), mDropDownHorizontalOffset, mDropDownVerticalOffset); mDropDownList.setSelection(ListView.INVALID_POSITION); - mDropDownList.hideSelector(); - mDropDownList.requestFocus(); + clearListSelection(); post(mHideSelector); } } @@ -1106,6 +1112,18 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe mDropDownList.setOnItemClickListener(mDropDownItemClickListener); mDropDownList.setFocusable(true); mDropDownList.setFocusableInTouchMode(true); + mDropDownList.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { + public void onItemSelected(AdapterView<?> parent, View view, + int position, long id) { + + if (position != -1) { + mDropDownList.mListSelectionHidden = false; + } + } + + public void onNothingSelected(AdapterView<?> parent) { + } + }); if (mItemSelectedListener != null) { mDropDownList.setOnItemSelectedListener(mItemSelectedListener); @@ -1229,10 +1247,7 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe private class ListSelectorHider implements Runnable { public void run() { - if (mDropDownList != null) { - mDropDownList.hideSelector(); - mDropDownList.requestLayout(); - } + clearListSelection(); } } @@ -1259,6 +1274,36 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe * passed to the drop down; the list only looks focused.</p> */ private static class DropDownListView extends ListView { + /* + * WARNING: This is a workaround for a touch mode issue. + * + * Touch mode is propagated lazily to windows. This causes problems in + * the following scenario: + * - Type something in the AutoCompleteTextView and get some results + * - Move down with the d-pad to select an item in the list + * - Move up with the d-pad until the selection disappears + * - Type more text in the AutoCompleteTextView *using the soft keyboard* + * and get new results; you are now in touch mode + * - The selection comes back on the first item in the list, even though + * the list is supposed to be in touch mode + * + * Using the soft keyboard triggers the touch mode change but that change + * is propagated to our window only after the first list layout, therefore + * after the list attempts to resurrect the selection. + * + * The trick to work around this issue is to pretend the list is in touch + * mode when we know that the selection should not appear, that is when + * we know the user moved the selection away from the list. + * + * This boolean is set to true whenever we explicitely hide the list's + * selection and reset to false whenver we know the user moved the + * selection back to the list. + * + * When this boolean is true, isInTouchMode() returns true, otherwise it + * returns super.isInTouchMode(). + */ + private boolean mListSelectionHidden; + /** * <p>Creates a new list view wrapper.</p> * @@ -1304,6 +1349,12 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe return mSelectionBottomPadding; } + @Override + public boolean isInTouchMode() { + // WARNING: Please read the comment where mListSelectionHidden is declared + return mListSelectionHidden || super.isInTouchMode(); + } + /** * <p>Returns the focus state in the drop down.</p> * diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java index a195ac7..6532125 100644 --- a/core/java/android/widget/ListView.java +++ b/core/java/android/widget/ListView.java @@ -174,6 +174,8 @@ public class ListView extends AbsListView { setDividerHeight(dividerHeight); } + setChoiceMode(a.getInt(R.styleable.ListView_choiceMode, CHOICE_MODE_NONE)); + mHeaderDividersEnabled = a.getBoolean(R.styleable.ListView_headerDividersEnabled, true); mFooterDividersEnabled = a.getBoolean(R.styleable.ListView_footerDividersEnabled, true); diff --git a/core/java/com/android/internal/backup/GoogleTransport.java b/core/java/com/android/internal/backup/GoogleTransport.java deleted file mode 100644 index c089c23..0000000 --- a/core/java/com/android/internal/backup/GoogleTransport.java +++ /dev/null @@ -1,50 +0,0 @@ -package com.android.internal.backup; - -import android.backup.RestoreSet; -import android.content.pm.PackageInfo; -import android.os.ParcelFileDescriptor; -import android.os.RemoteException; - -/** - * Backup transport for saving data to Google cloud storage. - */ - -public class GoogleTransport extends IBackupTransport.Stub { - - public long requestBackupTime() throws RemoteException { - return 0; // !!! TODO: implement real backoff policy - } - - public int startSession() throws RemoteException { - // TODO Auto-generated method stub - return 0; - } - - public int endSession() throws RemoteException { - // TODO Auto-generated method stub - return 0; - } - - public int performBackup(PackageInfo packageInfo, ParcelFileDescriptor data) - throws RemoteException { - // TODO Auto-generated method stub - return 0; - } - - // Restore handling - public RestoreSet[] getAvailableRestoreSets() throws android.os.RemoteException { - // !!! TODO: real implementation - return null; - } - - public PackageInfo[] getAppSet(int token) throws android.os.RemoteException { - // !!! TODO: real implementation - return new PackageInfo[0]; - } - - public int getRestoreData(int token, PackageInfo packageInfo, ParcelFileDescriptor data) - throws android.os.RemoteException { - // !!! TODO: real implementation - return 0; - } -} diff --git a/core/java/com/android/internal/backup/IBackupTransport.aidl b/core/java/com/android/internal/backup/IBackupTransport.aidl index 9daabca..84ed729 100644 --- a/core/java/com/android/internal/backup/IBackupTransport.aidl +++ b/core/java/com/android/internal/backup/IBackupTransport.aidl @@ -42,11 +42,14 @@ interface IBackupTransport { */ /** * Verify that this is a suitable time for a backup pass. This should return zero - * if a backup is reasonable right now, false otherwise. This method will be called - * outside of the {@link #startSession}/{@link #endSession} pair. + * if a backup is reasonable right now, some positive value otherwise. This method + * will be called outside of the {@link #startSession}/{@link #endSession} pair. * - * <p>If this is not a suitable time for a backup, the transport should suggest a + * <p>If this is not a suitable time for a backup, the transport should return a * backoff delay, in milliseconds, after which the Backup Manager should try again. + * + * @return Zero if this is a suitable time for a backup pass, or a positive time delay + * in milliseconds to suggest deferring the backup pass for a while. */ long requestBackupTime(); diff --git a/core/java/com/android/internal/backup/LocalTransport.java b/core/java/com/android/internal/backup/LocalTransport.java index f7b89f2..3ef8666 100644 --- a/core/java/com/android/internal/backup/LocalTransport.java +++ b/core/java/com/android/internal/backup/LocalTransport.java @@ -12,6 +12,8 @@ import android.os.ParcelFileDescriptor; import android.os.RemoteException; import android.util.Log; +import org.bouncycastle.util.encoders.Base64; + import java.io.File; import java.io.FileFilter; import java.io.FileInputStream; @@ -78,25 +80,35 @@ public class LocalTransport extends IBackupTransport.Stub { byte[] buf = new byte[bufSize]; while (changeSet.readNextHeader()) { String key = changeSet.getKey(); + String base64Key = new String(Base64.encode(key.getBytes())); + File entityFile = new File(packageDir, base64Key); + int dataSize = changeSet.getDataSize(); - if (DEBUG) Log.v(TAG, "Got change set key=" + key + " size=" + dataSize); - if (dataSize > bufSize) { - bufSize = dataSize; - buf = new byte[bufSize]; - } - changeSet.readEntityData(buf, dataSize); - if (DEBUG) Log.v(TAG, " + data size " + dataSize); - - File entityFile = new File(packageDir, key); - FileOutputStream entity = new FileOutputStream(entityFile); - try { - entity.write(buf, 0, dataSize); - } catch (IOException e) { - Log.e(TAG, "Unable to update key file " - + entityFile.getAbsolutePath()); - err = -1; - } finally { - entity.close(); + + if (DEBUG) Log.v(TAG, "Got change set key=" + key + " size=" + dataSize + + " key64=" + base64Key); + + if (dataSize >= 0) { + FileOutputStream entity = new FileOutputStream(entityFile); + + if (dataSize > bufSize) { + bufSize = dataSize; + buf = new byte[bufSize]; + } + changeSet.readEntityData(buf, 0, dataSize); + if (DEBUG) Log.v(TAG, " data size " + dataSize); + + try { + entity.write(buf, 0, dataSize); + } catch (IOException e) { + Log.e(TAG, "Unable to update key file " + + entityFile.getAbsolutePath()); + err = -1; + } finally { + entity.close(); + } + } else { + entityFile.delete(); } } } catch (IOException e) { @@ -112,11 +124,9 @@ public class LocalTransport extends IBackupTransport.Stub { // Restore handling public RestoreSet[] getAvailableRestoreSets() throws android.os.RemoteException { // one hardcoded restore set - RestoreSet[] set = new RestoreSet[1]; - set[0].device = "flash"; - set[0].name = "Local disk image"; - set[0].token = 0; - return set; + RestoreSet set = new RestoreSet("Local disk image", "flash", 0); + RestoreSet[] array = { set }; + return array; } public PackageInfo[] getAppSet(int token) throws android.os.RemoteException { @@ -162,14 +172,15 @@ public class LocalTransport extends IBackupTransport.Stub { File[] blobs = packageDir.listFiles(); int err = 0; if (blobs != null && blobs.length > 0) { - BackupDataOutput out = new BackupDataOutput(mContext, outFd.getFileDescriptor()); + BackupDataOutput out = new BackupDataOutput(outFd.getFileDescriptor()); try { for (File f : blobs) { FileInputStream in = new FileInputStream(f); int size = (int) f.length(); byte[] buf = new byte[size]; in.read(buf); - out.writeEntityHeader(f.getName(), size); + String key = new String(Base64.decode(f.getName())); + out.writeEntityHeader(key, size); out.writeEntityData(buf, size); } } catch (Exception e) { diff --git a/core/jni/Android.mk b/core/jni/Android.mk index 9066233..251bc84 100644 --- a/core/jni/Android.mk +++ b/core/jni/Android.mk @@ -11,6 +11,10 @@ else LOCAL_CFLAGS += -DPACKED="" endif +ifeq ($(WITH_JIT),true) + LOCAL_CFLAGS += -DWITH_JIT +endif + ifneq ($(USE_CUSTOM_RUNTIME_HEAP_MAX),) LOCAL_CFLAGS += -DCUSTOM_RUNTIME_HEAP_MAX=$(USE_CUSTOM_RUNTIME_HEAP_MAX) endif @@ -119,7 +123,8 @@ LOCAL_SRC_FILES:= \ com_android_internal_graphics_NativeUtils.cpp \ android_backup_BackupDataInput.cpp \ android_backup_BackupDataOutput.cpp \ - android_backup_FileBackupHelper.cpp + android_backup_FileBackupHelper.cpp \ + android_backup_RestoreHelperBase.cpp LOCAL_C_INCLUDES += \ $(JNI_H_INCLUDE) \ diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index 4512fef..1a1ebb4 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -156,6 +156,7 @@ extern int register_android_location_GpsLocationProvider(JNIEnv* env); extern int register_android_backup_BackupDataInput(JNIEnv *env); extern int register_android_backup_BackupDataOutput(JNIEnv *env); extern int register_android_backup_FileBackupHelper(JNIEnv *env); +extern int register_android_backup_RestoreHelperBase(JNIEnv *env); static AndroidRuntime* gCurRuntime = NULL; @@ -528,7 +529,14 @@ int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv) bool checkJni = false; bool checkDexSum = false; bool logStdio = false; - enum { kEMDefault, kEMIntPortable, kEMIntFast } executionMode = kEMDefault; + enum { + kEMDefault, + kEMIntPortable, + kEMIntFast, +#if defined(WITH_JIT) + kEMJitCompiler, +#endif + } executionMode = kEMDefault; property_get("dalvik.vm.checkjni", propBuf, ""); @@ -547,6 +555,10 @@ int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv) executionMode = kEMIntPortable; } else if (strcmp(propBuf, "int:fast") == 0) { executionMode = kEMIntFast; +#if defined(WITH_JIT) + } else if (strcmp(propBuf, "int:jit") == 0) { + executionMode = kEMJitCompiler; +#endif } property_get("dalvik.vm.stack-trace-file", stackTraceFileBuf, ""); @@ -683,12 +695,70 @@ int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv) //opt.optionString = "-verbose:jni"; //mOptions.add(opt); } + +#if defined(WITH_JIT) + /* Minimal profile threshold to trigger JIT compilation */ + char jitThresholdBuf[sizeof("-Xthreshold:") + PROPERTY_VALUE_MAX]; + property_get("dalvik.vm.jit.threshold", propBuf, ""); + if (strlen(propBuf) > 0) { + strcpy(jitThresholdBuf, "-Xthreshold:"); + strcat(jitThresholdBuf, propBuf); + opt.optionString = jitThresholdBuf; + mOptions.add(opt); + } + + /* Force interpreter-only mode for selected opcodes. Eg "1-0a,3c,f1-ff" */ + char jitOpBuf[sizeof("-Xjitop:") + PROPERTY_VALUE_MAX]; + property_get("dalvik.vm.jit.op", propBuf, ""); + if (strlen(propBuf) > 0) { + strcpy(jitOpBuf, "-Xjitop:"); + strcat(jitOpBuf, propBuf); + opt.optionString = jitOpBuf; + mOptions.add(opt); + } + + /* + * Reverse the polarity of dalvik.vm.jit.op and force interpreter-only + * for non-selected opcodes. + */ + property_get("dalvik.vm.jit.includeop", propBuf, ""); + if (strlen(propBuf) > 0) { + opt.optionString = "-Xincludeselectedop"; + mOptions.add(opt); + } + + /* Force interpreter-only mode for selected methods */ + char jitMethodBuf[sizeof("-Xjitmethod:") + PROPERTY_VALUE_MAX]; + property_get("dalvik.vm.jit.method", propBuf, ""); + if (strlen(propBuf) > 0) { + strcpy(jitMethodBuf, "-Xjitmethod:"); + strcat(jitMethodBuf, propBuf); + opt.optionString = jitMethodBuf; + mOptions.add(opt); + } + + /* + * Reverse the polarity of dalvik.vm.jit.method and force interpreter-only + * for non-selected methods. + */ + property_get("dalvik.vm.jit.includemethod", propBuf, ""); + if (strlen(propBuf) > 0) { + opt.optionString = "-Xincludeselectedmethod"; + mOptions.add(opt); + } +#endif + if (executionMode == kEMIntPortable) { opt.optionString = "-Xint:portable"; mOptions.add(opt); } else if (executionMode == kEMIntFast) { opt.optionString = "-Xint:fast"; mOptions.add(opt); +#if defined(WITH_JIT) + } else if (executionMode == kEMJitCompiler) { + opt.optionString = "-Xint:jit"; + mOptions.add(opt); +#endif } if (checkDexSum) { @@ -1172,6 +1242,7 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_backup_BackupDataInput), REG_JNI(register_android_backup_BackupDataOutput), REG_JNI(register_android_backup_FileBackupHelper), + REG_JNI(register_android_backup_RestoreHelperBase), }; /* diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp index 76e6f02..d1fe83e 100644 --- a/core/jni/android/graphics/Paint.cpp +++ b/core/jni/android/graphics/Paint.cpp @@ -69,6 +69,8 @@ public: static void reset(JNIEnv* env, jobject clazz, SkPaint* obj) { obj->reset(); + // utf16 is required for java + obj->setTextEncoding(SkPaint::kUTF16_TextEncoding); } static void assign(JNIEnv* env, jobject clazz, SkPaint* dst, const SkPaint* src) { diff --git a/core/jni/android_backup_BackupDataInput.cpp b/core/jni/android_backup_BackupDataInput.cpp index 5b2fb73..cf8a8e8 100644 --- a/core/jni/android_backup_BackupDataInput.cpp +++ b/core/jni/android_backup_BackupDataInput.cpp @@ -55,18 +55,13 @@ static jint readNextHeader_native(JNIEnv* env, jobject clazz, int r, jobject entity) { int err; + bool done; BackupDataReader* reader = (BackupDataReader*)r; - err = reader->Status(); - if (err != 0) { - return err < 0 ? err : -1; - } - int type = 0; - err = reader->ReadNextHeader(&type); - if (err == EIO) { - // Clean EOF with no footer block; just claim we're done + err = reader->ReadNextHeader(&done, &type); + if (done) { return 1; } @@ -75,24 +70,12 @@ readNextHeader_native(JNIEnv* env, jobject clazz, int r, jobject entity) } switch (type) { - case BACKUP_HEADER_APP_V1: - { - String8 packageName; - int cookie; - err = reader->ReadAppHeader(&packageName, &cookie); - if (err != 0) { - LOGD("ReadAppHeader() returned %d; aborting", err); - return err < 0 ? err : -1; - } - break; - } case BACKUP_HEADER_ENTITY_V1: { String8 key; size_t dataSize; err = reader->ReadEntityHeader(&key, &dataSize); if (err != 0) { - LOGD("ReadEntityHeader(); aborting", err); return err < 0 ? err : -1; } // TODO: Set the fields in the entity object @@ -101,10 +84,6 @@ readNextHeader_native(JNIEnv* env, jobject clazz, int r, jobject entity) env->SetIntField(entity, s_dataSizeField, dataSize); return 0; } - case BACKUP_FOOTER_APP_V1: - { - break; - } default: LOGD("Unknown header type: 0x%08x\n", type); return -1; @@ -115,12 +94,12 @@ readNextHeader_native(JNIEnv* env, jobject clazz, int r, jobject entity) } static jint -readEntityData_native(JNIEnv* env, jobject clazz, int r, jbyteArray data, int size) +readEntityData_native(JNIEnv* env, jobject clazz, int r, jbyteArray data, int offset, int size) { int err; BackupDataReader* reader = (BackupDataReader*)r; - if (env->GetArrayLength(data) < size) { + if (env->GetArrayLength(data) < (size+offset)) { // size mismatch return -1; } @@ -130,19 +109,31 @@ readEntityData_native(JNIEnv* env, jobject clazz, int r, jbyteArray data, int si return -2; } - err = reader->ReadEntityData(dataBytes, size); + err = reader->ReadEntityData(dataBytes+offset, size); env->ReleaseByteArrayElements(data, dataBytes, 0); return err; } +static jint +skipEntityData_native(JNIEnv* env, jobject clazz, int r) +{ + int err; + BackupDataReader* reader = (BackupDataReader*)r; + + err = reader->SkipEntityData(); + + return err; +} + static const JNINativeMethod g_methods[] = { { "ctor", "(Ljava/io/FileDescriptor;)I", (void*)ctor_native }, { "dtor", "(I)V", (void*)dtor_native }, { "readNextHeader_native", "(ILandroid/backup/BackupDataInput$EntityHeader;)I", (void*)readNextHeader_native }, - { "readEntityData_native", "(I[BI)I", (void*)readEntityData_native }, + { "readEntityData_native", "(I[BII)I", (void*)readEntityData_native }, + { "skipEntityData_native", "(I)I", (void*)skipEntityData_native }, }; int register_android_backup_BackupDataInput(JNIEnv* env) diff --git a/core/jni/android_backup_RestoreHelperBase.cpp b/core/jni/android_backup_RestoreHelperBase.cpp new file mode 100644 index 0000000..3173420 --- /dev/null +++ b/core/jni/android_backup_RestoreHelperBase.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. + */ + +#define LOG_TAG "FileBackupHelper_native" +#include <utils/Log.h> + +#include "JNIHelp.h" +#include <android_runtime/AndroidRuntime.h> + +#include <utils/BackupHelpers.h> + +namespace android +{ + +// java.io.FileDescriptor +static jfieldID s_descriptorField = 0; + +static int +ctor(JNIEnv* env, jobject clazz) +{ + return (int)new RestoreHelperBase(); +} + +static void +dtor(JNIEnv* env, jobject clazz, jint ptr) +{ + delete (RestoreHelperBase*)ptr; +} + +static int +writeFile_native(JNIEnv* env, jobject clazz, jint ptr, jstring filenameObj, int backupReaderPtr) +{ + int err; + RestoreHelperBase* restore = (RestoreHelperBase*)ptr; + BackupDataReader* reader = (BackupDataReader*)backupReaderPtr; + char const* filename; + + filename = env->GetStringUTFChars(filenameObj, NULL); + + err = restore->WriteFile(String8(filename), reader); + + env->ReleaseStringUTFChars(filenameObj, filename); + + return err; +} + +static int +writeSnapshot_native(JNIEnv* env, jobject clazz, jint ptr, jobject fileDescriptor) +{ + int err; + + RestoreHelperBase* restore = (RestoreHelperBase*)ptr; + int fd = env->GetIntField(fileDescriptor, s_descriptorField); + + err = restore->WriteSnapshot(fd); + + return err; +} + +static const JNINativeMethod g_methods[] = { + { "ctor", "()I", (void*)ctor }, + { "dtor", "(I)V", (void*)dtor }, + { "writeFile_native", "(ILjava/lang/String;I)I", (void*)writeFile_native }, + { "writeSnapshot_native", "(ILjava/io/FileDescriptor;)I", (void*)writeSnapshot_native }, +}; + +int register_android_backup_RestoreHelperBase(JNIEnv* env) +{ + jclass clazz; + + clazz = env->FindClass("java/io/FileDescriptor"); + LOG_FATAL_IF(clazz == NULL, "Unable to find class java.io.FileDescriptor"); + s_descriptorField = env->GetFieldID(clazz, "descriptor", "I"); + LOG_FATAL_IF(s_descriptorField == NULL, + "Unable to find descriptor field in java.io.FileDescriptor"); + + return AndroidRuntime::registerNativeMethods(env, "android/backup/RestoreHelperBase", + g_methods, NELEM(g_methods)); +} + +} diff --git a/core/jni/android_location_GpsLocationProvider.cpp b/core/jni/android_location_GpsLocationProvider.cpp index 5c4fb22..bf0bd65 100644..100755 --- a/core/jni/android_location_GpsLocationProvider.cpp +++ b/core/jni/android_location_GpsLocationProvider.cpp @@ -336,13 +336,15 @@ static void android_location_GpsLocationProvider_agps_data_conn_failed(JNIEnv* e } static void android_location_GpsLocationProvider_set_agps_server(JNIEnv* env, jobject obj, - jint type, jint addr, jint port) + jint type, jstring hostname, jint port) { if (!sAGpsInterface) { sAGpsInterface = (const AGpsInterface*)sGpsInterface->get_extension(AGPS_INTERFACE); } if (sAGpsInterface) { - sAGpsInterface->set_server(type, addr, port); + const char *c_hostname = env->GetStringUTFChars(hostname, NULL); + sAGpsInterface->set_server(type, c_hostname, port); + env->ReleaseStringUTFChars(hostname, c_hostname); } } @@ -365,7 +367,7 @@ static JNINativeMethod sMethods[] = { {"native_agps_data_conn_open", "(Ljava/lang/String;)V", (void*)android_location_GpsLocationProvider_agps_data_conn_open}, {"native_agps_data_conn_closed", "()V", (void*)android_location_GpsLocationProvider_agps_data_conn_closed}, {"native_agps_data_conn_failed", "()V", (void*)android_location_GpsLocationProvider_agps_data_conn_failed}, - {"native_set_agps_server", "(III)V", (void*)android_location_GpsLocationProvider_set_agps_server}, + {"native_set_agps_server", "(ILjava/lang/String;I)V", (void*)android_location_GpsLocationProvider_set_agps_server}, }; int register_android_location_GpsLocationProvider(JNIEnv* env) diff --git a/core/jni/android_os_MemoryFile.cpp b/core/jni/android_os_MemoryFile.cpp index 8643393..1ae3ec7 100644 --- a/core/jni/android_os_MemoryFile.cpp +++ b/core/jni/android_os_MemoryFile.cpp @@ -118,7 +118,7 @@ static void android_os_MemoryFile_pin(JNIEnv* env, jobject clazz, jobject fileDe } } -static jboolean android_os_MemoryFile_is_ashmem_region(JNIEnv* env, jobject clazz, +static jint android_os_MemoryFile_get_mapped_size(JNIEnv* env, jobject clazz, jobject fileDescriptor) { int fd = jniGetFDFromFileDescriptor(env, fileDescriptor); // Use ASHMEM_GET_SIZE to find out if the fd refers to an ashmem region. @@ -129,13 +129,13 @@ static jboolean android_os_MemoryFile_is_ashmem_region(JNIEnv* env, jobject claz if (errno == ENOTTY) { // ENOTTY means that the ioctl does not apply to this object, // i.e., it is not an ashmem region. - return JNI_FALSE; + return (jint) -1; } // Some other error, throw exception jniThrowIOException(env, errno); - return JNI_FALSE; + return (jint) -1; } - return JNI_TRUE; + return (jint) result; } static const JNINativeMethod methods[] = { @@ -146,8 +146,8 @@ static const JNINativeMethod methods[] = { {"native_read", "(Ljava/io/FileDescriptor;I[BIIIZ)I", (void*)android_os_MemoryFile_read}, {"native_write", "(Ljava/io/FileDescriptor;I[BIIIZ)V", (void*)android_os_MemoryFile_write}, {"native_pin", "(Ljava/io/FileDescriptor;Z)V", (void*)android_os_MemoryFile_pin}, - {"native_is_ashmem_region", "(Ljava/io/FileDescriptor;)Z", - (void*)android_os_MemoryFile_is_ashmem_region} + {"native_get_mapped_size", "(Ljava/io/FileDescriptor;)I", + (void*)android_os_MemoryFile_get_mapped_size} }; static const char* const kClassPathName = "android/os/MemoryFile"; diff --git a/core/jni/android_server_BluetoothA2dpService.cpp b/core/jni/android_server_BluetoothA2dpService.cpp index 153d16e..14da1fd 100644 --- a/core/jni/android_server_BluetoothA2dpService.cpp +++ b/core/jni/android_server_BluetoothA2dpService.cpp @@ -79,6 +79,7 @@ static bool initNative(JNIEnv* env, jobject object) { dbus_error_free(&err); return false; } + dbus_connection_set_exit_on_disconnect(nat->conn, FALSE); #endif /*HAVE_BLUETOOTH*/ return true; } diff --git a/core/jni/android_server_BluetoothDeviceService.cpp b/core/jni/android_server_BluetoothDeviceService.cpp index 16bfc9c..b02a19b 100644 --- a/core/jni/android_server_BluetoothDeviceService.cpp +++ b/core/jni/android_server_BluetoothDeviceService.cpp @@ -113,6 +113,7 @@ static bool initializeNativeDataNative(JNIEnv* env, jobject object) { dbus_error_free(&err); return false; } + dbus_connection_set_exit_on_disconnect(nat->conn, FALSE); #endif /*HAVE_BLUETOOTH*/ return true; } diff --git a/core/jni/android_server_BluetoothEventLoop.cpp b/core/jni/android_server_BluetoothEventLoop.cpp index e0ea788..0857cb3 100644 --- a/core/jni/android_server_BluetoothEventLoop.cpp +++ b/core/jni/android_server_BluetoothEventLoop.cpp @@ -117,6 +117,7 @@ static void initializeNativeDataNative(JNIEnv* env, jobject object) { LOGE("%s: Could not get onto the system bus!", __FUNCTION__); dbus_error_free(&err); } + dbus_connection_set_exit_on_disconnect(nat->conn, FALSE); } #endif } @@ -148,6 +149,19 @@ static const DBusObjectPathVTable agent_vtable = { NULL, agent_event_filter, NULL, NULL, NULL, NULL }; +static unsigned int unix_events_to_dbus_flags(short events) { + return (events & DBUS_WATCH_READABLE ? POLLIN : 0) | + (events & DBUS_WATCH_WRITABLE ? POLLOUT : 0) | + (events & DBUS_WATCH_ERROR ? POLLERR : 0) | + (events & DBUS_WATCH_HANGUP ? POLLHUP : 0); +} + +static short dbus_flags_to_unix_events(unsigned int flags) { + return (flags & POLLIN ? DBUS_WATCH_READABLE : 0) | + (flags & POLLOUT ? DBUS_WATCH_WRITABLE : 0) | + (flags & POLLERR ? DBUS_WATCH_ERROR : 0) | + (flags & POLLHUP ? DBUS_WATCH_HANGUP : 0); +} static jboolean setUpEventLoop(native_data_t *nat) { LOGV(__FUNCTION__); @@ -407,8 +421,7 @@ static void handleWatchAdd(native_data_t *nat) { read(nat->controlFdR, &newFD, sizeof(int)); read(nat->controlFdR, &flags, sizeof(unsigned int)); read(nat->controlFdR, &watch, sizeof(DBusWatch *)); - int events = (flags & DBUS_WATCH_READABLE ? POLLIN : 0) - | (flags & DBUS_WATCH_WRITABLE ? POLLOUT : 0); + short events = dbus_flags_to_unix_events(flags); for (int y = 0; y<nat->pollMemberCount; y++) { if ((nat->pollData[y].fd == newFD) && @@ -452,8 +465,7 @@ static void handleWatchRemove(native_data_t *nat) { read(nat->controlFdR, &removeFD, sizeof(int)); read(nat->controlFdR, &flags, sizeof(unsigned int)); - int events = (flags & DBUS_WATCH_READABLE ? POLLIN : 0) - | (flags & DBUS_WATCH_WRITABLE ? POLLOUT : 0); + short events = dbus_flags_to_unix_events(flags); for (int y = 0; y < nat->pollMemberCount; y++) { if ((nat->pollData[y].fd == removeFD) && @@ -517,13 +529,12 @@ static void *eventLoopMain(void *ptr) { } } } else { - int event = nat->pollData[i].revents; - int flags = (event & POLLIN ? DBUS_WATCH_READABLE : 0) | - (event & POLLOUT ? DBUS_WATCH_WRITABLE : 0); - dbus_watch_handle(nat->watchData[i], event); - nat->pollData[i].revents = 0; - // can only do one - it may have caused a 'remove' - break; + short events = nat->pollData[i].revents; + unsigned int flags = unix_events_to_dbus_flags(events); + dbus_watch_handle(nat->watchData[i], flags); + nat->pollData[i].revents = 0; + // can only do one - it may have caused a 'remove' + break; } } while (dbus_connection_dispatch(nat->conn) == diff --git a/core/res/res/drawable/call_contact.png b/core/res/res/drawable/call_contact.png Binary files differnew file mode 100644 index 0000000..1abeb5d --- /dev/null +++ b/core/res/res/drawable/call_contact.png diff --git a/core/res/res/drawable/create_contact.png b/core/res/res/drawable/create_contact.png Binary files differnew file mode 100644 index 0000000..5c5718b --- /dev/null +++ b/core/res/res/drawable/create_contact.png diff --git a/core/res/res/drawable/search_dropdown_background_apps.9.png b/core/res/res/drawable/search_dropdown_background_apps.9.png Binary files differindex 56b697d..804260a 100644 --- a/core/res/res/drawable/search_dropdown_background_apps.9.png +++ b/core/res/res/drawable/search_dropdown_background_apps.9.png diff --git a/core/res/res/layout/recent_apps_dialog.xml b/core/res/res/layout/recent_apps_dialog.xml index 852b2f1..c4ee95d 100644 --- a/core/res/res/layout/recent_apps_dialog.xml +++ b/core/res/res/layout/recent_apps_dialog.xml @@ -17,67 +17,63 @@ */ --> -<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" - android:layout_height="wrap_content"> - + android:layout_height="wrap_content" + android:padding="3dip" + android:orientation="vertical"> + + <!-- This is only intended to be visible when all buttons (below) are invisible --> + <TextView + android:id="@+id/no_applications_message" + android:layout_width="285dip" + android:layout_height="wrap_content" + android:layout_marginTop="15dip" + android:layout_marginBottom="15dip" + android:gravity="center" + android:textAppearance="?android:attr/textAppearanceMedium" + android:text="@android:string/no_recent_tasks" /> + + <!-- The first row has a fixed-width because the UI spec requires the box + to display with full-width no matter how many icons are visible, but to + adjust height based on number of rows. --> + <!-- TODO Adjust all sizes, padding, etc. to meet pixel-perfect specs --> + <LinearLayout + android:layout_width="285dip" + android:layout_height="wrap_content" + android:orientation="horizontal" > + + <include + layout="@android:layout/recent_apps_icon" + android:id="@+id/button1" /> + + <include + layout="@android:layout/recent_apps_icon" + android:id="@+id/button2" /> + + <include + layout="@android:layout/recent_apps_icon" + android:id="@+id/button3" /> + + </LinearLayout> + <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" - android:padding="3dip" - android:orientation="vertical"> - - <!-- This is only intended to be visible when all buttons (below) are invisible --> - <TextView - android:id="@+id/no_applications_message" - android:layout_width="285dip" - android:layout_height="wrap_content" - android:layout_marginTop="15dip" - android:layout_marginBottom="15dip" - android:gravity="center" - android:textAppearance="?android:attr/textAppearanceMedium" - android:text="@android:string/no_recent_tasks" /> - - <!-- The first row has a fixed-width because the UI spec requires the box - to display with full-width no matter how many icons are visible, but to - adjust height based on number of rows. --> - <!-- TODO Adjust all sizes, padding, etc. to meet pixel-perfect specs --> - <LinearLayout - android:layout_width="285dip" - android:layout_height="wrap_content" - android:orientation="horizontal" > + android:orientation="horizontal" > + + <include + layout="@android:layout/recent_apps_icon" + android:id="@+id/button4" /> + + <include + layout="@android:layout/recent_apps_icon" + android:id="@+id/button5" /> + + <include + layout="@android:layout/recent_apps_icon" + android:id="@+id/button6" /> - <include - layout="@android:layout/recent_apps_icon" - android:id="@+id/button1" /> - - <include - layout="@android:layout/recent_apps_icon" - android:id="@+id/button2" /> - - <include - layout="@android:layout/recent_apps_icon" - android:id="@+id/button3" /> - - </LinearLayout> - - <LinearLayout - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:orientation="horizontal" > - - <include - layout="@android:layout/recent_apps_icon" - android:id="@+id/button4" /> - - <include - layout="@android:layout/recent_apps_icon" - android:id="@+id/button5" /> - - <include - layout="@android:layout/recent_apps_icon" - android:id="@+id/button6" /> - - </LinearLayout> </LinearLayout> -</FrameLayout> +</LinearLayout>
\ No newline at end of file diff --git a/core/res/res/layout/recent_apps_icon.xml b/core/res/res/layout/recent_apps_icon.xml index b8cf089..d32643c 100644 --- a/core/res/res/layout/recent_apps_icon.xml +++ b/core/res/res/layout/recent_apps_icon.xml @@ -18,27 +18,22 @@ --> <!-- This is not a standalone element - it is imported into recent_apps_dialog.xml --> - -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" +<TextView + xmlns:android="http://schemas.android.com/apk/res/android" + android:id="@+id/label" + style="?android:attr/buttonStyle" + android:background="@drawable/btn_application_selector" android:layout_width="87dip" - android:layout_height="78dip" + android:layout_height="88dip" android:layout_margin="3dip" - android:orientation="vertical" - android:gravity="center_vertical" - style="?android:attr/buttonStyle" - android:background="@drawable/btn_application_selector"> - <ImageView android:id="@+id/icon" - android:layout_width="@android:dimen/app_icon_size" - android:layout_height="@android:dimen/app_icon_size" - android:layout_gravity="center_horizontal" - android:scaleType="fitCenter" /> - <TextView android:id="@+id/label" - android:layout_width="fill_parent" - android:layout_height="wrap_content" - android:textSize="12dip" - android:maxLines="1" - android:ellipsize="end" - android:duplicateParentState="true" - android:textColor="@color/primary_text_dark_focused" - android:gravity="center_horizontal" /> -</LinearLayout> + android:textColor="@color/primary_text_dark_focused" + + android:paddingTop="5dip" + android:paddingBottom="2dip" + android:drawablePadding="0dip" + + android:textSize="13dip" + android:maxLines="2" + android:ellipsize="marquee" + android:fadingEdge="horizontal" + android:gravity="top|center_horizontal" /> diff --git a/core/res/res/values-ar-rEG/donottranslate-cldr.xml b/core/res/res/values-ar-rEG/donottranslate-cldr.xml index 4826a41..2c20ffc 100644 --- a/core/res/res/values-ar-rEG/donottranslate-cldr.xml +++ b/core/res/res/values-ar-rEG/donottranslate-cldr.xml @@ -143,4 +143,5 @@ <string name="same_year_mdy1_mdy2">%3$s %2$s - %8$s %7$s، %9$s</string> <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> </resources> diff --git a/core/res/res/values-bg-rBG/donottranslate-cldr.xml b/core/res/res/values-bg-rBG/donottranslate-cldr.xml index 010b974..cda072a 100644 --- a/core/res/res/values-bg-rBG/donottranslate-cldr.xml +++ b/core/res/res/values-bg-rBG/donottranslate-cldr.xml @@ -143,4 +143,5 @@ <string name="same_year_mdy1_mdy2">%3$s %2$s - %8$s %7$s %9$s</string> <string name="same_month_mdy1_mdy2">%3$s-%8$s %2$s %9$s</string> <string name="same_year_wday1_mdy1_wday2_mdy2">%3$s %2$s %9$s, %1$s - %8$s %7$s y, %6$s</string> + <string name="short_format_month">%b</string> </resources> diff --git a/core/res/res/values-ca-rES/donottranslate-cldr.xml b/core/res/res/values-ca-rES/donottranslate-cldr.xml index 4eabba7..d5abeef 100644 --- a/core/res/res/values-ca-rES/donottranslate-cldr.xml +++ b/core/res/res/values-ca-rES/donottranslate-cldr.xml @@ -143,4 +143,5 @@ <string name="same_year_mdy1_mdy2">%3$s de %2$s - %8$s de %7$s de %9$s</string> <string name="same_month_mdy1_mdy2">%3$s-%8$s de %2$s de %9$s</string> <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s %3$s de %2$s - %6$s %8$s de %7$s de %9$s</string> + <string name="short_format_month">%b</string> </resources> diff --git a/core/res/res/values-cs-rCZ/donottranslate-cldr.xml b/core/res/res/values-cs-rCZ/donottranslate-cldr.xml index 0670080..e933f20 100644 --- a/core/res/res/values-cs-rCZ/donottranslate-cldr.xml +++ b/core/res/res/values-cs-rCZ/donottranslate-cldr.xml @@ -107,10 +107,10 @@ <string name="abbrev_month_day_year">%-e.%-m.%Y</string> <string name="month_day">%-e. %B</string> <string name="month">%-B</string> - <string name="month_year">%Y %B</string> - <string name="abbrev_month_day">%b %-e</string> - <string name="abbrev_month">%-b</string> - <string name="abbrev_month_year">%Y %b</string> + <string name="month_year">%-B %Y</string> + <string name="abbrev_month_day">%-e.%-m</string> + <string name="abbrev_month">%-B</string> + <string name="abbrev_month_year">%-B %Y</string> <string name="time1_time2">%1$s - %2$s</string> <string name="date1_date2">%2$s - %5$s</string> <string name="numeric_md1_md2">%3$s.%2$s. - %8$s.%7$s.</string> @@ -121,26 +121,27 @@ <string name="numeric_md1_time1_md2_time2">%5$s %3$s.%2$s - %10$s %8$s.%7$s</string> <string name="numeric_wday1_md1_time1_wday2_md2_time2">%5$s %1$s, %2$s-%3$s - %10$s %6$s, %7$s-%8$s</string> <string name="numeric_mdy1_time1_mdy2_time2">%5$s %3$s.%2$s.%4$s - %10$s %8$s.%7$s.%9$s</string> - <string name="wday1_date1_time1_wday2_date2_time2">%3$s %1$s %2$s - %6$s %4$s %5$s</string> - <string name="wday1_date1_wday2_date2">%1$s %2$s - %4$s %5$s</string> + <string name="wday1_date1_time1_wday2_date2_time2">%3$s %1$s, %2$s - %6$s %4$s, %5$s</string> + <string name="wday1_date1_wday2_date2">%1$s, %2$s - %4$s, %5$s</string> <string name="date1_time1_date2_time2">%3$s %2$s - %6$s %5$s</string> - <string name="time_wday_date">%1$s %2$s %3$s</string> - <string name="wday_date">%2$s %3$s</string> + <string name="time_wday_date">%1$s %2$s, %3$s</string> + <string name="wday_date">%2$s, %3$s</string> <string name="time_wday">%1$s %2$s</string> <string name="same_year_md1_md2">%3$s. %2$s - %8$s. %7$s</string> - <string name="same_year_wday1_md1_wday2_md2">%1$s %2$s %3$s - %6$s %7$s %8$s</string> + <string name="same_year_wday1_md1_wday2_md2">%1$s, %3$s. %2$s - %6$s, %8$s. %7$s</string> <string name="same_year_md1_time1_md2_time2">%5$s %3$s. %2$s - %10$s %8$s. %7$s</string> <string name="same_month_md1_time1_md2_time2">%5$s %3$s. %2$s - %10$s %8$s. %7$s</string> - <string name="same_year_wday1_md1_time1_wday2_md2_time2">%5$s %1$s %2$s %3$s - %10$s %6$s %7$s %8$s</string> - <string name="same_month_wday1_md1_time1_wday2_md2_time2">%5$s %1$s %2$s %3$s - %10$s %6$s %7$s %8$s</string> + <string name="same_year_wday1_md1_time1_wday2_md2_time2">%5$s %1$s, %3$s. %2$s - %10$s %6$s, %8$s. %7$s</string> + <string name="same_month_wday1_md1_time1_wday2_md2_time2">%5$s %1$s, %3$s. %2$s - %10$s %6$s, %8$s. %7$s</string> <string name="same_year_mdy1_time1_mdy2_time2">%5$s %3$s. %2$s %4$s - %10$s %8$s. %7$s %9$s</string> <string name="same_month_mdy1_time1_mdy2_time2">%5$s %3$s. %2$s %4$s - %10$s %8$s. %7$s %9$s</string> - <string name="same_year_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s, %4$s %2$s %3$s - %10$s %6$s, %9$s %7$s %8$s</string> - <string name="same_month_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s, %4$s %2$s %3$s - %10$s %6$s, %9$s %7$s %8$s</string> - <string name="same_month_wday1_mdy1_wday2_mdy2">%1$s, %4$s %2$s %3$s - %6$s, %9$s %7$s %8$s</string> + <string name="same_year_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s, %3$s. %2$s %4$s - %10$s %6$s, %8$s. %7$s %9$s</string> + <string name="same_month_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s, %3$s. %2$s %4$s - %10$s %6$s, %8$s. %7$s %9$s</string> + <string name="same_month_wday1_mdy1_wday2_mdy2">%1$s, %3$s. %2$s %4$s - %6$s, %8$s. %7$s %9$s</string> <string name="same_month_md1_md2">%3$s.-%8$s. %2$s</string> - <string name="same_month_wday1_md1_wday2_md2">%1$s %2$s %3$s - %6$s %7$s %8$s</string> + <string name="same_month_wday1_md1_wday2_md2">%1$s, %3$s. %2$s - %6$s, %8$s. %7$s</string> <string name="same_year_mdy1_mdy2">%3$s. %2$s - %8$s. %7$s %9$s</string> <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> </resources> diff --git a/core/res/res/values-cs/donottranslate-cldr.xml b/core/res/res/values-cs/donottranslate-cldr.xml index 0670080..e933f20 100644 --- a/core/res/res/values-cs/donottranslate-cldr.xml +++ b/core/res/res/values-cs/donottranslate-cldr.xml @@ -107,10 +107,10 @@ <string name="abbrev_month_day_year">%-e.%-m.%Y</string> <string name="month_day">%-e. %B</string> <string name="month">%-B</string> - <string name="month_year">%Y %B</string> - <string name="abbrev_month_day">%b %-e</string> - <string name="abbrev_month">%-b</string> - <string name="abbrev_month_year">%Y %b</string> + <string name="month_year">%-B %Y</string> + <string name="abbrev_month_day">%-e.%-m</string> + <string name="abbrev_month">%-B</string> + <string name="abbrev_month_year">%-B %Y</string> <string name="time1_time2">%1$s - %2$s</string> <string name="date1_date2">%2$s - %5$s</string> <string name="numeric_md1_md2">%3$s.%2$s. - %8$s.%7$s.</string> @@ -121,26 +121,27 @@ <string name="numeric_md1_time1_md2_time2">%5$s %3$s.%2$s - %10$s %8$s.%7$s</string> <string name="numeric_wday1_md1_time1_wday2_md2_time2">%5$s %1$s, %2$s-%3$s - %10$s %6$s, %7$s-%8$s</string> <string name="numeric_mdy1_time1_mdy2_time2">%5$s %3$s.%2$s.%4$s - %10$s %8$s.%7$s.%9$s</string> - <string name="wday1_date1_time1_wday2_date2_time2">%3$s %1$s %2$s - %6$s %4$s %5$s</string> - <string name="wday1_date1_wday2_date2">%1$s %2$s - %4$s %5$s</string> + <string name="wday1_date1_time1_wday2_date2_time2">%3$s %1$s, %2$s - %6$s %4$s, %5$s</string> + <string name="wday1_date1_wday2_date2">%1$s, %2$s - %4$s, %5$s</string> <string name="date1_time1_date2_time2">%3$s %2$s - %6$s %5$s</string> - <string name="time_wday_date">%1$s %2$s %3$s</string> - <string name="wday_date">%2$s %3$s</string> + <string name="time_wday_date">%1$s %2$s, %3$s</string> + <string name="wday_date">%2$s, %3$s</string> <string name="time_wday">%1$s %2$s</string> <string name="same_year_md1_md2">%3$s. %2$s - %8$s. %7$s</string> - <string name="same_year_wday1_md1_wday2_md2">%1$s %2$s %3$s - %6$s %7$s %8$s</string> + <string name="same_year_wday1_md1_wday2_md2">%1$s, %3$s. %2$s - %6$s, %8$s. %7$s</string> <string name="same_year_md1_time1_md2_time2">%5$s %3$s. %2$s - %10$s %8$s. %7$s</string> <string name="same_month_md1_time1_md2_time2">%5$s %3$s. %2$s - %10$s %8$s. %7$s</string> - <string name="same_year_wday1_md1_time1_wday2_md2_time2">%5$s %1$s %2$s %3$s - %10$s %6$s %7$s %8$s</string> - <string name="same_month_wday1_md1_time1_wday2_md2_time2">%5$s %1$s %2$s %3$s - %10$s %6$s %7$s %8$s</string> + <string name="same_year_wday1_md1_time1_wday2_md2_time2">%5$s %1$s, %3$s. %2$s - %10$s %6$s, %8$s. %7$s</string> + <string name="same_month_wday1_md1_time1_wday2_md2_time2">%5$s %1$s, %3$s. %2$s - %10$s %6$s, %8$s. %7$s</string> <string name="same_year_mdy1_time1_mdy2_time2">%5$s %3$s. %2$s %4$s - %10$s %8$s. %7$s %9$s</string> <string name="same_month_mdy1_time1_mdy2_time2">%5$s %3$s. %2$s %4$s - %10$s %8$s. %7$s %9$s</string> - <string name="same_year_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s, %4$s %2$s %3$s - %10$s %6$s, %9$s %7$s %8$s</string> - <string name="same_month_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s, %4$s %2$s %3$s - %10$s %6$s, %9$s %7$s %8$s</string> - <string name="same_month_wday1_mdy1_wday2_mdy2">%1$s, %4$s %2$s %3$s - %6$s, %9$s %7$s %8$s</string> + <string name="same_year_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s, %3$s. %2$s %4$s - %10$s %6$s, %8$s. %7$s %9$s</string> + <string name="same_month_wday1_mdy1_time1_wday2_mdy2_time2">%5$s %1$s, %3$s. %2$s %4$s - %10$s %6$s, %8$s. %7$s %9$s</string> + <string name="same_month_wday1_mdy1_wday2_mdy2">%1$s, %3$s. %2$s %4$s - %6$s, %8$s. %7$s %9$s</string> <string name="same_month_md1_md2">%3$s.-%8$s. %2$s</string> - <string name="same_month_wday1_md1_wday2_md2">%1$s %2$s %3$s - %6$s %7$s %8$s</string> + <string name="same_month_wday1_md1_wday2_md2">%1$s, %3$s. %2$s - %6$s, %8$s. %7$s</string> <string name="same_year_mdy1_mdy2">%3$s. %2$s - %8$s. %7$s %9$s</string> <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> </resources> diff --git a/core/res/res/values-da-rDK/donottranslate-cldr.xml b/core/res/res/values-da-rDK/donottranslate-cldr.xml index 4a2a656..8cef5b2 100644 --- a/core/res/res/values-da-rDK/donottranslate-cldr.xml +++ b/core/res/res/values-da-rDK/donottranslate-cldr.xml @@ -143,4 +143,5 @@ <string name="same_year_mdy1_mdy2">%3$s. %2$s - %8$s. %7$s %9$s</string> <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 den %3$s. %2$s - %6$s den %8$s. %7$s %9$s</string> + <string name="short_format_month">%b</string> </resources> diff --git a/core/res/res/values-de-rAT/donottranslate-cldr.xml b/core/res/res/values-de-rAT/donottranslate-cldr.xml index e6112ba..559e1ee 100644 --- a/core/res/res/values-de-rAT/donottranslate-cldr.xml +++ b/core/res/res/values-de-rAT/donottranslate-cldr.xml @@ -110,4 +110,5 @@ <string name="same_year_mdy1_mdy2">%3$s. %2$s - %8$s. %7$s %9$s</string> <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> </resources> diff --git a/core/res/res/values-de-rCH/donottranslate-cldr.xml b/core/res/res/values-de-rCH/donottranslate-cldr.xml index b611c08..2ca6787 100644 --- a/core/res/res/values-de-rCH/donottranslate-cldr.xml +++ b/core/res/res/values-de-rCH/donottranslate-cldr.xml @@ -143,4 +143,5 @@ <string name="same_year_mdy1_mdy2">%3$s. %2$s - %8$s. %7$s %9$s</string> <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> </resources> diff --git a/core/res/res/values-de-rDE/donottranslate-cldr.xml b/core/res/res/values-de-rDE/donottranslate-cldr.xml index b611c08..2ca6787 100644 --- a/core/res/res/values-de-rDE/donottranslate-cldr.xml +++ b/core/res/res/values-de-rDE/donottranslate-cldr.xml @@ -143,4 +143,5 @@ <string name="same_year_mdy1_mdy2">%3$s. %2$s - %8$s. %7$s %9$s</string> <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> </resources> diff --git a/core/res/res/values-de-rLI/donottranslate-cldr.xml b/core/res/res/values-de-rLI/donottranslate-cldr.xml index b611c08..2ca6787 100644 --- a/core/res/res/values-de-rLI/donottranslate-cldr.xml +++ b/core/res/res/values-de-rLI/donottranslate-cldr.xml @@ -143,4 +143,5 @@ <string name="same_year_mdy1_mdy2">%3$s. %2$s - %8$s. %7$s %9$s</string> <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> </resources> diff --git a/core/res/res/values-de/donottranslate-cldr.xml b/core/res/res/values-de/donottranslate-cldr.xml index b611c08..2ca6787 100644 --- a/core/res/res/values-de/donottranslate-cldr.xml +++ b/core/res/res/values-de/donottranslate-cldr.xml @@ -143,4 +143,5 @@ <string name="same_year_mdy1_mdy2">%3$s. %2$s - %8$s. %7$s %9$s</string> <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> </resources> diff --git a/core/res/res/values-el-rGR/donottranslate-cldr.xml b/core/res/res/values-el-rGR/donottranslate-cldr.xml index f76281a..e8f02fb 100644 --- a/core/res/res/values-el-rGR/donottranslate-cldr.xml +++ b/core/res/res/values-el-rGR/donottranslate-cldr.xml @@ -143,4 +143,5 @@ <string name="same_year_mdy1_mdy2">%3$s %2$s - %8$s %7$s %9$s</string> <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> </resources> diff --git a/core/res/res/values-en-rAU/donottranslate-cldr.xml b/core/res/res/values-en-rAU/donottranslate-cldr.xml index 223a22b..5d1a8f6 100644 --- a/core/res/res/values-en-rAU/donottranslate-cldr.xml +++ b/core/res/res/values-en-rAU/donottranslate-cldr.xml @@ -143,4 +143,5 @@ <string name="same_year_mdy1_mdy2">%3$s %2$s - %8$s %7$s %9$s</string> <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> </resources> diff --git a/core/res/res/values-en-rCA/donottranslate-cldr.xml b/core/res/res/values-en-rCA/donottranslate-cldr.xml index 32fa2b0..5d7300e 100644 --- a/core/res/res/values-en-rCA/donottranslate-cldr.xml +++ b/core/res/res/values-en-rCA/donottranslate-cldr.xml @@ -143,4 +143,5 @@ <string name="same_year_mdy1_mdy2">%2$s %3$s - %7$s %8$s, %9$s</string> <string name="same_month_mdy1_mdy2">%2$s %3$s-%8$s, %9$s</string> <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s, %2$s %3$s - %6$s, %7$s %8$s, %9$s</string> + <string name="short_format_month">%b</string> </resources> diff --git a/core/res/res/values-en-rGB/donottranslate-cldr.xml b/core/res/res/values-en-rGB/donottranslate-cldr.xml index b90112f..b115c6e 100644 --- a/core/res/res/values-en-rGB/donottranslate-cldr.xml +++ b/core/res/res/values-en-rGB/donottranslate-cldr.xml @@ -143,4 +143,5 @@ <string name="same_year_mdy1_mdy2">%3$s %2$s - %8$s %7$s %9$s</string> <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> </resources> diff --git a/core/res/res/values-en-rIE/donottranslate-cldr.xml b/core/res/res/values-en-rIE/donottranslate-cldr.xml index 4143da5..15fc8e8 100644 --- a/core/res/res/values-en-rIE/donottranslate-cldr.xml +++ b/core/res/res/values-en-rIE/donottranslate-cldr.xml @@ -143,4 +143,5 @@ <string name="same_year_mdy1_mdy2">%3$s %2$s - %8$s %7$s %9$s</string> <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> </resources> diff --git a/core/res/res/values-en-rIN/donottranslate-cldr.xml b/core/res/res/values-en-rIN/donottranslate-cldr.xml index 6522d67..2507ee8 100644 --- a/core/res/res/values-en-rIN/donottranslate-cldr.xml +++ b/core/res/res/values-en-rIN/donottranslate-cldr.xml @@ -143,4 +143,5 @@ <string name="same_year_mdy1_mdy2">%3$s %2$s - %8$s %7$s %9$s</string> <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> </resources> diff --git a/core/res/res/values-en-rNZ/donottranslate-cldr.xml b/core/res/res/values-en-rNZ/donottranslate-cldr.xml index d29455a..07d4fe8 100644 --- a/core/res/res/values-en-rNZ/donottranslate-cldr.xml +++ b/core/res/res/values-en-rNZ/donottranslate-cldr.xml @@ -143,4 +143,5 @@ <string name="same_year_mdy1_mdy2">%3$s %2$s - %8$s %7$s %9$s</string> <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> </resources> diff --git a/core/res/res/values-en-rSG/donottranslate-cldr.xml b/core/res/res/values-en-rSG/donottranslate-cldr.xml index 56c58e2..286cc0e 100644 --- a/core/res/res/values-en-rSG/donottranslate-cldr.xml +++ b/core/res/res/values-en-rSG/donottranslate-cldr.xml @@ -143,4 +143,5 @@ <string name="same_year_mdy1_mdy2">%2$s %3$s – %7$s %8$s, %9$s</string> <string name="same_month_mdy1_mdy2">%2$s %3$s – %8$s, %9$s</string> <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s, %2$s %3$s – %6$s, %7$s %8$s, %9$s</string> + <string name="short_format_month">%b</string> </resources> diff --git a/core/res/res/values-en-rUS/donottranslate-cldr.xml b/core/res/res/values-en-rUS/donottranslate-cldr.xml index 56c58e2..286cc0e 100644 --- a/core/res/res/values-en-rUS/donottranslate-cldr.xml +++ b/core/res/res/values-en-rUS/donottranslate-cldr.xml @@ -143,4 +143,5 @@ <string name="same_year_mdy1_mdy2">%2$s %3$s – %7$s %8$s, %9$s</string> <string name="same_month_mdy1_mdy2">%2$s %3$s – %8$s, %9$s</string> <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s, %2$s %3$s – %6$s, %7$s %8$s, %9$s</string> + <string name="short_format_month">%b</string> </resources> diff --git a/core/res/res/values-en-rZA/donottranslate-cldr.xml b/core/res/res/values-en-rZA/donottranslate-cldr.xml index e1aac04..9e8681b 100644 --- a/core/res/res/values-en-rZA/donottranslate-cldr.xml +++ b/core/res/res/values-en-rZA/donottranslate-cldr.xml @@ -143,4 +143,5 @@ <string name="same_year_mdy1_mdy2">%3$s %2$s - %8$s %7$s %9$s</string> <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> </resources> diff --git a/core/res/res/values-es-rES/donottranslate-cldr.xml b/core/res/res/values-es-rES/donottranslate-cldr.xml index b516291..c1dc58b 100644 --- a/core/res/res/values-es-rES/donottranslate-cldr.xml +++ b/core/res/res/values-es-rES/donottranslate-cldr.xml @@ -143,4 +143,5 @@ <string name="same_year_mdy1_mdy2">%3$s %2$s – %8$s %7$s %9$s</string> <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> </resources> diff --git a/core/res/res/values-es-rUS/donottranslate-cldr.xml b/core/res/res/values-es-rUS/donottranslate-cldr.xml index 587a615..24923c3 100644 --- a/core/res/res/values-es-rUS/donottranslate-cldr.xml +++ b/core/res/res/values-es-rUS/donottranslate-cldr.xml @@ -143,4 +143,5 @@ <string name="same_year_mdy1_mdy2">%3$s de %2$s al %8$s de %7$s de %9$s</string> <string name="same_month_mdy1_mdy2">%3$s-%8$s de %2$s de %9$s</string> <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s %3$s de %2$s al %6$s %8$s de %7$s de %9$s</string> + <string name="short_format_month">%b</string> </resources> diff --git a/core/res/res/values-es/donottranslate-cldr.xml b/core/res/res/values-es/donottranslate-cldr.xml index b516291..c1dc58b 100644 --- a/core/res/res/values-es/donottranslate-cldr.xml +++ b/core/res/res/values-es/donottranslate-cldr.xml @@ -143,4 +143,5 @@ <string name="same_year_mdy1_mdy2">%3$s %2$s – %8$s %7$s %9$s</string> <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> </resources> diff --git a/core/res/res/values-fi-rFI/donottranslate-cldr.xml b/core/res/res/values-fi-rFI/donottranslate-cldr.xml index 40cda53..dd12e57 100644 --- a/core/res/res/values-fi-rFI/donottranslate-cldr.xml +++ b/core/res/res/values-fi-rFI/donottranslate-cldr.xml @@ -143,4 +143,5 @@ <string name="same_year_mdy1_mdy2">%3$s. %2$s – %8$s. %7$s %9$s</string> <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> </resources> diff --git a/core/res/res/values-fr-rBE/donottranslate-cldr.xml b/core/res/res/values-fr-rBE/donottranslate-cldr.xml index 0795cc5..ea4ecf2 100644 --- a/core/res/res/values-fr-rBE/donottranslate-cldr.xml +++ b/core/res/res/values-fr-rBE/donottranslate-cldr.xml @@ -143,4 +143,5 @@ <string name="same_year_mdy1_mdy2">%3$s %2$s au %8$s %7$s %9$s</string> <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 au %6$s %8$s %7$s %9$s</string> + <string name="short_format_month">%b</string> </resources> diff --git a/core/res/res/values-fr-rCA/donottranslate-cldr.xml b/core/res/res/values-fr-rCA/donottranslate-cldr.xml index 7802ee0..346b971 100644 --- a/core/res/res/values-fr-rCA/donottranslate-cldr.xml +++ b/core/res/res/values-fr-rCA/donottranslate-cldr.xml @@ -143,4 +143,5 @@ <string name="same_year_mdy1_mdy2">du %3$s %2$s au %8$s %7$s %9$s</string> <string name="same_month_mdy1_mdy2">%3$s–%8$s %2$s %9$s</string> <string name="same_year_wday1_mdy1_wday2_mdy2">du %1$s %3$s %2$s au %6$s %8$s %7$s %9$s</string> + <string name="short_format_month">%b</string> </resources> diff --git a/core/res/res/values-fr-rCH/donottranslate-cldr.xml b/core/res/res/values-fr-rCH/donottranslate-cldr.xml index bbda44a..0a9835f 100644 --- a/core/res/res/values-fr-rCH/donottranslate-cldr.xml +++ b/core/res/res/values-fr-rCH/donottranslate-cldr.xml @@ -143,4 +143,5 @@ <string name="same_year_mdy1_mdy2">%3$s %2$s au %8$s %7$s %9$s</string> <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 au %6$s, %8$s %7$s %9$s</string> + <string name="short_format_month">%b</string> </resources> diff --git a/core/res/res/values-fr-rFR/donottranslate-cldr.xml b/core/res/res/values-fr-rFR/donottranslate-cldr.xml index 76d4141..f340e83 100644 --- a/core/res/res/values-fr-rFR/donottranslate-cldr.xml +++ b/core/res/res/values-fr-rFR/donottranslate-cldr.xml @@ -143,4 +143,5 @@ <string name="same_year_mdy1_mdy2">%3$s %2$s – %8$s %7$s %9$s</string> <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> </resources> diff --git a/core/res/res/values-fr/donottranslate-cldr.xml b/core/res/res/values-fr/donottranslate-cldr.xml index 76d4141..f340e83 100644 --- a/core/res/res/values-fr/donottranslate-cldr.xml +++ b/core/res/res/values-fr/donottranslate-cldr.xml @@ -143,4 +143,5 @@ <string name="same_year_mdy1_mdy2">%3$s %2$s – %8$s %7$s %9$s</string> <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> </resources> diff --git a/core/res/res/values-he-rIL/donottranslate-cldr.xml b/core/res/res/values-he-rIL/donottranslate-cldr.xml index 1c8a6f7..e3feb1e 100644 --- a/core/res/res/values-he-rIL/donottranslate-cldr.xml +++ b/core/res/res/values-he-rIL/donottranslate-cldr.xml @@ -143,4 +143,5 @@ <string name="same_year_mdy1_mdy2">%3$s %2$s - %8$s %7$s %9$s</string> <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> </resources> diff --git a/core/res/res/values-hi-rIN/donottranslate-cldr.xml b/core/res/res/values-hi-rIN/donottranslate-cldr.xml index ba4ded7..03da515 100644 --- a/core/res/res/values-hi-rIN/donottranslate-cldr.xml +++ b/core/res/res/values-hi-rIN/donottranslate-cldr.xml @@ -143,4 +143,5 @@ <string name="same_year_mdy1_mdy2">%9$s-%2$s-%3$s – %7$s-%8$s</string> <string name="same_month_mdy1_mdy2">%9$s-%2$s-%3$s – %8$s</string> <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s, %9$s-%2$s-%3$s – %6$s, yyyy-%7$s-%8$s</string> + <string name="short_format_month">%b</string> </resources> diff --git a/core/res/res/values-hu-rHU/donottranslate-cldr.xml b/core/res/res/values-hu-rHU/donottranslate-cldr.xml index 8dcb426..b56f520 100644 --- a/core/res/res/values-hu-rHU/donottranslate-cldr.xml +++ b/core/res/res/values-hu-rHU/donottranslate-cldr.xml @@ -143,4 +143,5 @@ <string name="same_year_mdy1_mdy2">%9$s. %2$s %3$s. - %7$s %8$s.</string> <string name="same_month_mdy1_mdy2">%9$s. %2$s %3$s-%8$s.</string> <string name="same_year_wday1_mdy1_wday2_mdy2">%9$s. %2$s %3$s., %1$s - %7$s %8$s., %6$s</string> + <string name="short_format_month">%b</string> </resources> diff --git a/core/res/res/values-id-rID/donottranslate-cldr.xml b/core/res/res/values-id-rID/donottranslate-cldr.xml index feac981..22860a7 100644 --- a/core/res/res/values-id-rID/donottranslate-cldr.xml +++ b/core/res/res/values-id-rID/donottranslate-cldr.xml @@ -143,4 +143,5 @@ <string name="same_year_mdy1_mdy2">%9$s-%2$s-%3$s – %7$s-%8$s</string> <string name="same_month_mdy1_mdy2">%9$s-%2$s-%3$s – %8$s</string> <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s, %9$s-%2$s-%3$s – %6$s, yyyy-%7$s-%8$s</string> + <string name="short_format_month">%b</string> </resources> diff --git a/core/res/res/values-it-rCH/donottranslate-cldr.xml b/core/res/res/values-it-rCH/donottranslate-cldr.xml index 6b76f8e..6d9b550 100644 --- a/core/res/res/values-it-rCH/donottranslate-cldr.xml +++ b/core/res/res/values-it-rCH/donottranslate-cldr.xml @@ -138,4 +138,5 @@ <string name="same_year_mdy1_mdy2">%3$s %2$s - %8$s %7$s %9$s</string> <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> </resources> diff --git a/core/res/res/values-it-rIT/donottranslate-cldr.xml b/core/res/res/values-it-rIT/donottranslate-cldr.xml index 929f899..1bf7992 100644 --- a/core/res/res/values-it-rIT/donottranslate-cldr.xml +++ b/core/res/res/values-it-rIT/donottranslate-cldr.xml @@ -138,4 +138,5 @@ <string name="same_year_mdy1_mdy2">%3$s %2$s - %8$s %7$s %9$s</string> <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> </resources> diff --git a/core/res/res/values-it/donottranslate-cldr.xml b/core/res/res/values-it/donottranslate-cldr.xml index 929f899..1bf7992 100644 --- a/core/res/res/values-it/donottranslate-cldr.xml +++ b/core/res/res/values-it/donottranslate-cldr.xml @@ -138,4 +138,5 @@ <string name="same_year_mdy1_mdy2">%3$s %2$s - %8$s %7$s %9$s</string> <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> </resources> diff --git a/core/res/res/values-ja-rJP/donottranslate-cldr.xml b/core/res/res/values-ja-rJP/donottranslate-cldr.xml index ae07433..10f2f79 100644 --- a/core/res/res/values-ja-rJP/donottranslate-cldr.xml +++ b/core/res/res/values-ja-rJP/donottranslate-cldr.xml @@ -143,4 +143,5 @@ <string name="same_year_mdy1_mdy2">%9$s年%2$s%3$s日~%7$s月%8$s日</string> <string name="same_month_mdy1_mdy2">%9$s年%2$s%3$s日~%8$s日</string> <string name="same_year_wday1_mdy1_wday2_mdy2">%9$s年%2$s%3$s日(%1$s)~%7$s月%8$s日(%6$s)</string> + <string name="short_format_month">%b</string> </resources> diff --git a/core/res/res/values-ja/donottranslate-cldr.xml b/core/res/res/values-ja/donottranslate-cldr.xml index ae07433..10f2f79 100644 --- a/core/res/res/values-ja/donottranslate-cldr.xml +++ b/core/res/res/values-ja/donottranslate-cldr.xml @@ -143,4 +143,5 @@ <string name="same_year_mdy1_mdy2">%9$s年%2$s%3$s日~%7$s月%8$s日</string> <string name="same_month_mdy1_mdy2">%9$s年%2$s%3$s日~%8$s日</string> <string name="same_year_wday1_mdy1_wday2_mdy2">%9$s年%2$s%3$s日(%1$s)~%7$s月%8$s日(%6$s)</string> + <string name="short_format_month">%b</string> </resources> diff --git a/core/res/res/values-ko-rKR/donottranslate-cldr.xml b/core/res/res/values-ko-rKR/donottranslate-cldr.xml index 4ec1ba4..45ca90a 100644 --- a/core/res/res/values-ko-rKR/donottranslate-cldr.xml +++ b/core/res/res/values-ko-rKR/donottranslate-cldr.xml @@ -131,4 +131,5 @@ <string name="same_year_mdy1_mdy2">%9$s년 %2$s %3$s일 ~ %7$s월 %8$s일</string> <string name="same_month_mdy1_mdy2">%9$s년 %2$s %3$s일~%8$s일</string> <string name="same_year_wday1_mdy1_wday2_mdy2">%9$s년 %2$s %3$s일 %1$s ~ %7$s월 %8$s일 %6$s</string> + <string name="short_format_month">%b</string> </resources> diff --git a/core/res/res/values-ko/donottranslate-cldr.xml b/core/res/res/values-ko/donottranslate-cldr.xml index 4ec1ba4..45ca90a 100644 --- a/core/res/res/values-ko/donottranslate-cldr.xml +++ b/core/res/res/values-ko/donottranslate-cldr.xml @@ -131,4 +131,5 @@ <string name="same_year_mdy1_mdy2">%9$s년 %2$s %3$s일 ~ %7$s월 %8$s일</string> <string name="same_month_mdy1_mdy2">%9$s년 %2$s %3$s일~%8$s일</string> <string name="same_year_wday1_mdy1_wday2_mdy2">%9$s년 %2$s %3$s일 %1$s ~ %7$s월 %8$s일 %6$s</string> + <string name="short_format_month">%b</string> </resources> diff --git a/core/res/res/values-lt-rLT/donottranslate-cldr.xml b/core/res/res/values-lt-rLT/donottranslate-cldr.xml index 17f228d..636a180 100644 --- a/core/res/res/values-lt-rLT/donottranslate-cldr.xml +++ b/core/res/res/values-lt-rLT/donottranslate-cldr.xml @@ -143,4 +143,5 @@ <string name="same_year_mdy1_mdy2">%9$s m. %2$s %3$s d. - %7$s %8$s d.</string> <string name="same_month_mdy1_mdy2">%9$s m. %2$s %3$s d.-%8$s d.</string> <string name="same_year_wday1_mdy1_wday2_mdy2">%9$s m. %2$s %3$s d.,%1$s - %7$s %8$s d.,%6$s</string> + <string name="short_format_month">%b</string> </resources> diff --git a/core/res/res/values-lv-rLV/donottranslate-cldr.xml b/core/res/res/values-lv-rLV/donottranslate-cldr.xml index e6c3b79..3dec1d2 100644 --- a/core/res/res/values-lv-rLV/donottranslate-cldr.xml +++ b/core/res/res/values-lv-rLV/donottranslate-cldr.xml @@ -143,4 +143,5 @@ <string name="same_year_mdy1_mdy2">%9$s. gada %3$s. %2$s - %8$s. %7$s</string> <string name="same_month_mdy1_mdy2">%9$s. gada %3$s.-%8$s. %2$s</string> <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s, %9$s. gada %3$s. %2$s - %6$s, y. gada %8$s. %7$s</string> + <string name="short_format_month">%b</string> </resources> diff --git a/core/res/res/values-nb/donottranslate-cldr.xml b/core/res/res/values-nb/donottranslate-cldr.xml index 17c9b24..ecf0111 100644 --- a/core/res/res/values-nb/donottranslate-cldr.xml +++ b/core/res/res/values-nb/donottranslate-cldr.xml @@ -143,4 +143,5 @@ <string name="same_year_mdy1_mdy2">%3$s. %2$s–%8$s. %7$s %9$s</string> <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> </resources> diff --git a/core/res/res/values-nl-rBE/donottranslate-cldr.xml b/core/res/res/values-nl-rBE/donottranslate-cldr.xml index 72a4694..5b4cbf7 100644 --- a/core/res/res/values-nl-rBE/donottranslate-cldr.xml +++ b/core/res/res/values-nl-rBE/donottranslate-cldr.xml @@ -143,4 +143,5 @@ <string name="same_year_mdy1_mdy2">%3$s %2$s - %8$s %7$s %9$s</string> <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> </resources> diff --git a/core/res/res/values-nl-rNL/donottranslate-cldr.xml b/core/res/res/values-nl-rNL/donottranslate-cldr.xml index 549d816..b6231b6 100644 --- a/core/res/res/values-nl-rNL/donottranslate-cldr.xml +++ b/core/res/res/values-nl-rNL/donottranslate-cldr.xml @@ -143,4 +143,5 @@ <string name="same_year_mdy1_mdy2">%3$s %2$s - %8$s %7$s %9$s</string> <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> </resources> diff --git a/core/res/res/values-nl/donottranslate-cldr.xml b/core/res/res/values-nl/donottranslate-cldr.xml index 549d816..b6231b6 100644 --- a/core/res/res/values-nl/donottranslate-cldr.xml +++ b/core/res/res/values-nl/donottranslate-cldr.xml @@ -143,4 +143,5 @@ <string name="same_year_mdy1_mdy2">%3$s %2$s - %8$s %7$s %9$s</string> <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> </resources> diff --git a/core/res/res/values-pl-rPL/donottranslate-cldr.xml b/core/res/res/values-pl-rPL/donottranslate-cldr.xml index c4bed0d..4ad17bf 100644 --- a/core/res/res/values-pl-rPL/donottranslate-cldr.xml +++ b/core/res/res/values-pl-rPL/donottranslate-cldr.xml @@ -143,4 +143,5 @@ <string name="same_year_mdy1_mdy2">%3$s %2$s - %8$s %7$s %9$s</string> <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> </resources> diff --git a/core/res/res/values-pl/donottranslate-cldr.xml b/core/res/res/values-pl/donottranslate-cldr.xml index c4bed0d..4ad17bf 100644 --- a/core/res/res/values-pl/donottranslate-cldr.xml +++ b/core/res/res/values-pl/donottranslate-cldr.xml @@ -143,4 +143,5 @@ <string name="same_year_mdy1_mdy2">%3$s %2$s - %8$s %7$s %9$s</string> <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> </resources> diff --git a/core/res/res/values-pt-rBR/donottranslate-cldr.xml b/core/res/res/values-pt-rBR/donottranslate-cldr.xml index 7b3304c..4729055 100644 --- a/core/res/res/values-pt-rBR/donottranslate-cldr.xml +++ b/core/res/res/values-pt-rBR/donottranslate-cldr.xml @@ -143,4 +143,5 @@ <string name="same_year_mdy1_mdy2">%3$s de %2$s - %8$s de %7$s de %9$s</string> <string name="same_month_mdy1_mdy2">%3$s-%8$s de %2$s de %9$s</string> <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s, %3$s de %2$s - %6$s, %8$s de %7$s de %9$s</string> + <string name="short_format_month">%b</string> </resources> diff --git a/core/res/res/values-pt-rPT/donottranslate-cldr.xml b/core/res/res/values-pt-rPT/donottranslate-cldr.xml index b14dbcb..be0fdb7 100644 --- a/core/res/res/values-pt-rPT/donottranslate-cldr.xml +++ b/core/res/res/values-pt-rPT/donottranslate-cldr.xml @@ -143,4 +143,5 @@ <string name="same_year_mdy1_mdy2">%3$s de %2$s - %8$s de %7$s de %9$s</string> <string name="same_month_mdy1_mdy2">%3$s-%8$s de %2$s de %9$s</string> <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s, %3$s de %2$s - %6$s, %8$s de %7$s de %9$s</string> + <string name="short_format_month">%b</string> </resources> diff --git a/core/res/res/values-pt/donottranslate-cldr.xml b/core/res/res/values-pt/donottranslate-cldr.xml index 7b3304c..4729055 100644 --- a/core/res/res/values-pt/donottranslate-cldr.xml +++ b/core/res/res/values-pt/donottranslate-cldr.xml @@ -143,4 +143,5 @@ <string name="same_year_mdy1_mdy2">%3$s de %2$s - %8$s de %7$s de %9$s</string> <string name="same_month_mdy1_mdy2">%3$s-%8$s de %2$s de %9$s</string> <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s, %3$s de %2$s - %6$s, %8$s de %7$s de %9$s</string> + <string name="short_format_month">%b</string> </resources> diff --git a/core/res/res/values-ro-rRO/donottranslate-cldr.xml b/core/res/res/values-ro-rRO/donottranslate-cldr.xml index 3672c94..4622445 100644 --- a/core/res/res/values-ro-rRO/donottranslate-cldr.xml +++ b/core/res/res/values-ro-rRO/donottranslate-cldr.xml @@ -143,4 +143,5 @@ <string name="same_year_mdy1_mdy2">%3$s %2$s - %8$s %7$s %9$s</string> <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> </resources> diff --git a/core/res/res/values-ru-rRU/donottranslate-cldr.xml b/core/res/res/values-ru-rRU/donottranslate-cldr.xml index 2b8c235..21c06ff 100644 --- a/core/res/res/values-ru-rRU/donottranslate-cldr.xml +++ b/core/res/res/values-ru-rRU/donottranslate-cldr.xml @@ -143,4 +143,5 @@ <string name="same_year_mdy1_mdy2">%3$s %2$s - %8$s %7$s %9$s г.</string> <string name="same_month_mdy1_mdy2">%3$s-%8$s %2$s %9$s г.</string> <string name="same_year_wday1_mdy1_wday2_mdy2">%3$s %2$s - %8$s %7$s %9$s г.</string> + <string name="short_format_month">%b</string> </resources> diff --git a/core/res/res/values-ru/donottranslate-cldr.xml b/core/res/res/values-ru/donottranslate-cldr.xml index 2b8c235..21c06ff 100644 --- a/core/res/res/values-ru/donottranslate-cldr.xml +++ b/core/res/res/values-ru/donottranslate-cldr.xml @@ -143,4 +143,5 @@ <string name="same_year_mdy1_mdy2">%3$s %2$s - %8$s %7$s %9$s г.</string> <string name="same_month_mdy1_mdy2">%3$s-%8$s %2$s %9$s г.</string> <string name="same_year_wday1_mdy1_wday2_mdy2">%3$s %2$s - %8$s %7$s %9$s г.</string> + <string name="short_format_month">%b</string> </resources> diff --git a/core/res/res/values-sk-rSK/donottranslate-cldr.xml b/core/res/res/values-sk-rSK/donottranslate-cldr.xml index ea2a5ab..2b3c6d9 100644 --- a/core/res/res/values-sk-rSK/donottranslate-cldr.xml +++ b/core/res/res/values-sk-rSK/donottranslate-cldr.xml @@ -143,4 +143,5 @@ <string name="same_year_mdy1_mdy2">%3$s. %2$s - %8$s. %7$s %9$s</string> <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> </resources> diff --git a/core/res/res/values-sl-rSI/donottranslate-cldr.xml b/core/res/res/values-sl-rSI/donottranslate-cldr.xml index b9c30c9..2b2b9c3 100644 --- a/core/res/res/values-sl-rSI/donottranslate-cldr.xml +++ b/core/res/res/values-sl-rSI/donottranslate-cldr.xml @@ -143,4 +143,5 @@ <string name="same_year_mdy1_mdy2">%3$s. %2$s. – %8$s. %7$s. %9$s</string> <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> </resources> diff --git a/core/res/res/values-sr-rRS/donottranslate-cldr.xml b/core/res/res/values-sr-rRS/donottranslate-cldr.xml index f88de6b..55ca968 100644 --- a/core/res/res/values-sr-rRS/donottranslate-cldr.xml +++ b/core/res/res/values-sr-rRS/donottranslate-cldr.xml @@ -143,4 +143,5 @@ <string name="same_year_mdy1_mdy2">%3$s. %2$s - %8$s. %7$s %9$s.</string> <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> </resources> diff --git a/core/res/res/values-sv-rSE/donottranslate-cldr.xml b/core/res/res/values-sv-rSE/donottranslate-cldr.xml index c846719..a6ffc9a 100644 --- a/core/res/res/values-sv-rSE/donottranslate-cldr.xml +++ b/core/res/res/values-sv-rSE/donottranslate-cldr.xml @@ -143,4 +143,5 @@ <string name="same_year_mdy1_mdy2">%3$s %2$s–%8$s %7$s %9$s</string> <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> </resources> diff --git a/core/res/res/values-th-rTH/donottranslate-cldr.xml b/core/res/res/values-th-rTH/donottranslate-cldr.xml index 53cd4d7..b3c76a3 100644 --- a/core/res/res/values-th-rTH/donottranslate-cldr.xml +++ b/core/res/res/values-th-rTH/donottranslate-cldr.xml @@ -143,4 +143,5 @@ <string name="same_year_mdy1_mdy2">%3$s %2$s – %8$s %7$s %9$s</string> <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> </resources> diff --git a/core/res/res/values-tr-rTR/donottranslate-cldr.xml b/core/res/res/values-tr-rTR/donottranslate-cldr.xml index 2475b21..d612305 100644 --- a/core/res/res/values-tr-rTR/donottranslate-cldr.xml +++ b/core/res/res/values-tr-rTR/donottranslate-cldr.xml @@ -143,4 +143,5 @@ <string name="same_year_mdy1_mdy2">%3$s %2$s - %8$s %7$s %9$s</string> <string name="same_month_mdy1_mdy2">%3$s-%8$s %2$s %9$s</string> <string name="same_year_wday1_mdy1_wday2_mdy2">%3$s %2$s %9$s %1$s - %8$s %7$s y %6$s</string> + <string name="short_format_month">%b</string> </resources> diff --git a/core/res/res/values-uk-rUA/donottranslate-cldr.xml b/core/res/res/values-uk-rUA/donottranslate-cldr.xml index 51dabd0..55d3983 100644 --- a/core/res/res/values-uk-rUA/donottranslate-cldr.xml +++ b/core/res/res/values-uk-rUA/donottranslate-cldr.xml @@ -143,4 +143,5 @@ <string name="same_year_mdy1_mdy2">%3$s %2$s – %8$s %7$s %9$s</string> <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> </resources> diff --git a/core/res/res/values-vi-rVN/donottranslate-cldr.xml b/core/res/res/values-vi-rVN/donottranslate-cldr.xml index 0d9eebc..71178cc 100644 --- a/core/res/res/values-vi-rVN/donottranslate-cldr.xml +++ b/core/res/res/values-vi-rVN/donottranslate-cldr.xml @@ -143,4 +143,5 @@ <string name="same_year_mdy1_mdy2">Ngày %3$s tháng %2$s - Ngày %8$s tháng %7$s năm %9$s</string> <string name="same_month_mdy1_mdy2">Ngày %3$s tháng %2$s - Ngày %8$s tháng M năm %9$s</string> <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s, ngày %3$s %2$s - %6$s, ngày %8$s %7$s năm %9$s</string> + <string name="short_format_month">%b</string> </resources> diff --git a/core/res/res/values-zh-rCN/donottranslate-cldr.xml b/core/res/res/values-zh-rCN/donottranslate-cldr.xml index 6685a7d..8c386a4 100644 --- a/core/res/res/values-zh-rCN/donottranslate-cldr.xml +++ b/core/res/res/values-zh-rCN/donottranslate-cldr.xml @@ -143,4 +143,5 @@ <string name="same_year_mdy1_mdy2">%9$s年%2$s%3$s日至%7$s月%8$s日</string> <string name="same_month_mdy1_mdy2">%9$s年%2$s%3$s日至%8$s日</string> <string name="same_year_wday1_mdy1_wday2_mdy2">%9$s年%2$s%3$s日%1$s至%7$s月%8$s日%6$s</string> + <string name="short_format_month">%b</string> </resources> diff --git a/core/res/res/values-zh-rTW/donottranslate-cldr.xml b/core/res/res/values-zh-rTW/donottranslate-cldr.xml index 6685a7d..8c386a4 100644 --- a/core/res/res/values-zh-rTW/donottranslate-cldr.xml +++ b/core/res/res/values-zh-rTW/donottranslate-cldr.xml @@ -143,4 +143,5 @@ <string name="same_year_mdy1_mdy2">%9$s年%2$s%3$s日至%7$s月%8$s日</string> <string name="same_month_mdy1_mdy2">%9$s年%2$s%3$s日至%8$s日</string> <string name="same_year_wday1_mdy1_wday2_mdy2">%9$s年%2$s%3$s日%1$s至%7$s月%8$s日%6$s</string> + <string name="short_format_month">%b</string> </resources> diff --git a/core/res/res/values/donottranslate-cldr.xml b/core/res/res/values/donottranslate-cldr.xml index 56c58e2..286cc0e 100644 --- a/core/res/res/values/donottranslate-cldr.xml +++ b/core/res/res/values/donottranslate-cldr.xml @@ -143,4 +143,5 @@ <string name="same_year_mdy1_mdy2">%2$s %3$s – %7$s %8$s, %9$s</string> <string name="same_month_mdy1_mdy2">%2$s %3$s – %8$s, %9$s</string> <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s, %2$s %3$s – %6$s, %7$s %8$s, %9$s</string> + <string name="short_format_month">%b</string> </resources> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 19fde5c..91b6609 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -228,6 +228,8 @@ <string name="httpErrorFileNotFound">The requested file was not found.</string> <!-- Displayed when a request failed because there are too many requests right now. --> <string name="httpErrorTooManyRequests">Too many requests are being processed. Try again later.</string> + <!-- Displayed a toast that a certificate is saved in the keystore --> + <string name="certificateSaved">The certificate is saved in the system\'s key store.</string> <!-- Account notifications --> <skip /> <!-- A notification is shown when the AccountManager is unable to @@ -1306,6 +1308,9 @@ <string name="battery_low_percent_format">less than <xliff:g id="number">%d%%</xliff:g> remaining.</string> + <!-- When the battery is low, this is the label of the button to go to the + power usage activity to find out what drained the battery. --> + <string name="battery_low_why">Why?</string> <!-- Title of the alert when something went wrong in the factory test. --> <string name="factorytest_failed">Factory test failed</string> diff --git a/graphics/java/android/graphics/BitmapFactory.java b/graphics/java/android/graphics/BitmapFactory.java index e5a9aab..2a39987 100644 --- a/graphics/java/android/graphics/BitmapFactory.java +++ b/graphics/java/android/graphics/BitmapFactory.java @@ -18,6 +18,7 @@ package android.graphics; import android.content.res.AssetManager; import android.content.res.Resources; +import android.os.MemoryFile; import android.util.DisplayMetrics; import android.util.TypedValue; @@ -435,6 +436,17 @@ public class BitmapFactory { * @return the decoded bitmap, or null */ public static Bitmap decodeFileDescriptor(FileDescriptor fd, Rect outPadding, Options opts) { + try { + if (MemoryFile.isMemoryFile(fd)) { + int mappedlength = MemoryFile.getMappedSize(fd); + MemoryFile file = new MemoryFile(fd, mappedlength, "r"); + InputStream is = file.getInputStream(); + return decodeStream(is, outPadding, opts); + } + } catch (IOException ex) { + // invalid filedescriptor, no need to call nativeDecodeFileDescriptor() + return null; + } return nativeDecodeFileDescriptor(fd, outPadding, opts); } @@ -447,7 +459,7 @@ public class BitmapFactory { * @return the decoded bitmap, or null */ public static Bitmap decodeFileDescriptor(FileDescriptor fd) { - return nativeDecodeFileDescriptor(fd, null, null); + return decodeFileDescriptor(fd, null, null); } private static native Bitmap nativeDecodeStream(InputStream is, byte[] storage, diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java index 06d53e3..4498e1a 100644 --- a/graphics/java/android/graphics/Canvas.java +++ b/graphics/java/android/graphics/Canvas.java @@ -1404,7 +1404,11 @@ public class Canvas { protected void finalize() throws Throwable { super.finalize(); - finalizer(mNativeCanvas); + // If the constructor threw an exception before setting mNativeCanvas, the native finalizer + // must not be invoked. + if (mNativeCanvas != 0) { + finalizer(mNativeCanvas); + } } /** diff --git a/include/tts/TtsEngine.h b/include/tts/TtsEngine.h index d2aa30e..8486532 100644 --- a/include/tts/TtsEngine.h +++ b/include/tts/TtsEngine.h @@ -99,14 +99,18 @@ public: // @param size length of the language value // @return TTS_SUCCESS, or TTS_FAILURE virtual tts_result loadLanguage(const char *value, const size_t size); - - // Signal the engine to use the specified language. This will force the - // language to be loaded if it wasn't loaded previously with loadLanguage(). - // See loadLanguage for the specification of the language. - // @param value pointer to the language value - // @param size length of the language value + + // Load the resources associated with the specified language, country and Locale variant. + // The loaded language will only be used once a call to setLanguageFromLocale() with the same + // language value is issued. Language and country values are coded according to the ISO three + // letter codes for languages and countries, as can be retrieved from a java.util.Locale + // instance. The variant value is encoded as the variant string retrieved from a + // java.util.Locale instance built with that variant data. + // @param lang pointer to the ISO three letter code for the language + // @param country pointer to the ISO three letter code for the country + // @param variant pointer to the variant code // @return TTS_SUCCESS, or TTS_FAILURE - virtual tts_result setLanguage(const char *value, const size_t size); + virtual tts_result setLanguage(const char *lang, const char *country, const char *variant); // Retrieve the currently set language, or an empty "value" if no language // has been set. diff --git a/include/utils/BackupHelpers.h b/include/utils/BackupHelpers.h index f60f4ea..c78b99a 100644 --- a/include/utils/BackupHelpers.h +++ b/include/utils/BackupHelpers.h @@ -19,33 +19,40 @@ #include <utils/Errors.h> #include <utils/String8.h> +#include <utils/KeyedVector.h> namespace android { enum { - BACKUP_HEADER_APP_V1 = 0x31707041, // App1 (little endian) BACKUP_HEADER_ENTITY_V1 = 0x61746144, // Data (little endian) - BACKUP_FOOTER_APP_V1 = 0x746f6f46, // Foot (little endian) }; -// the sizes of all of these match. -typedef struct { - int type; // == BACKUP_HEADER_APP_V1 - int packageLen; // length of the name of the package that follows, not including the null. - int cookie; -} app_header_v1; - typedef struct { int type; // BACKUP_HEADER_ENTITY_V1 int keyLen; // length of the key name, not including the null terminator int dataSize; // size of the data, not including the padding, -1 means delete } entity_header_v1; -typedef struct { - int type; // BACKUP_FOOTER_APP_V1 - int entityCount; // the number of entities that were written - int cookie; -} app_footer_v1; +struct SnapshotHeader { + int magic0; + int fileCount; + int magic1; + int totalSize; +}; + +struct FileState { + int modTime_sec; + int modTime_nsec; + int size; + int crc32; + int nameLen; +}; + +struct FileRec { + String8 file; + bool deleted; + FileState s; +}; /** @@ -61,13 +68,9 @@ public: // does not close fd ~BackupDataWriter(); - status_t WriteAppHeader(const String8& packageName, int cookie); - status_t WriteEntityHeader(const String8& key, size_t dataSize); status_t WriteEntityData(const void* data, size_t size); - status_t WriteAppFooter(int cookie); - private: explicit BackupDataWriter(); status_t write_padding_for(int n); @@ -92,34 +95,46 @@ public: ~BackupDataReader(); status_t Status(); - status_t ReadNextHeader(int* type = NULL); + status_t ReadNextHeader(bool* done, int* type); - status_t ReadAppHeader(String8* packageName, int* cookie); bool HasEntities(); status_t ReadEntityHeader(String8* key, size_t* dataSize); status_t SkipEntityData(); // must be called with the pointer at the begining of the data. - status_t ReadEntityData(void* data, size_t size); - status_t ReadAppFooter(int* cookie); + ssize_t ReadEntityData(void* data, size_t size); private: explicit BackupDataReader(); status_t skip_padding(); int m_fd; + bool m_done; status_t m_status; ssize_t m_pos; + ssize_t m_dataEndPos; int m_entityCount; union { int type; - app_header_v1 app; entity_header_v1 entity; - app_footer_v1 footer; } m_header; + String8 m_key; }; int back_up_files(int oldSnapshotFD, BackupDataWriter* dataStream, int newSnapshotFD, char const* const* files, char const* const *keys, int fileCount); +class RestoreHelperBase +{ +public: + RestoreHelperBase(); + ~RestoreHelperBase(); + + status_t WriteFile(const String8& filename, BackupDataReader* in); + status_t WriteSnapshot(int fd); + +private: + void* m_buf; + KeyedVector<String8,FileRec> m_files; +}; #define TEST_BACKUP_HELPERS 1 diff --git a/include/utils/ResourceTypes.h b/include/utils/ResourceTypes.h index 9b8c302..68f9233 100644 --- a/include/utils/ResourceTypes.h +++ b/include/utils/ResourceTypes.h @@ -1781,7 +1781,7 @@ public: void getLocales(Vector<String8>* locales) const; #ifndef HAVE_ANDROID_OS - void print() const; + void print(bool inclValues) const; #endif private: diff --git a/keystore/java/android/security/Keystore.java b/keystore/java/android/security/Keystore.java index 71c1cf4..2a3e6a7 100644 --- a/keystore/java/android/security/Keystore.java +++ b/keystore/java/android/security/Keystore.java @@ -30,6 +30,7 @@ public abstract class Keystore { return new FileKeystore(); } + // for compatiblity, start from here /** */ public abstract String getUserkey(String key); @@ -46,6 +47,34 @@ public abstract class Keystore { */ public abstract String[] getAllUserkeyKeys(); + // to here + + /** + */ + public abstract String getCaCertificate(String key); + + /** + */ + public abstract String getUserCertificate(String key); + + /** + */ + public abstract String getUserPrivateKey(String key); + + /** + * Returns the array of the certificate keynames in keystore if successful. + * Or return an empty array if error. + * + * @return array of the certificate keynames + */ + public abstract String[] getAllUserCertificateKeys(); + + /** + */ + public abstract String[] getAllCaCertificateKeys(); + + /** + */ public abstract String[] getSupportedKeyStrenghs(); /** @@ -59,17 +88,29 @@ public abstract class Keystore { public abstract String generateKeyPair( int keyStrengthIndex, String challenge, String organizations); - public abstract void addCertificate(String cert); + public abstract void addCertificate(byte[] cert); private static class FileKeystore extends Keystore { private static final String SERVICE_NAME = "keystore"; + private static final String LIST_CA_CERTIFICATES = "listcacerts"; + private static final String LIST_USER_CERTIFICATES = "listusercerts"; + private static final String GET_CA_CERTIFICATE = "getcacert"; + private static final String GET_USER_CERTIFICATE = "getusercert"; + private static final String GET_USER_KEY = "getuserkey"; + private static final String ADD_CA_CERTIFICATE = "addcacert"; + private static final String ADD_USER_CERTIFICATE = "addusercert"; + private static final String ADD_USER_KEY = "adduserkey"; + private static final String COMMAND_DELIMITER = "\t"; + private static final ServiceCommand mServiceCommand = + new ServiceCommand(SERVICE_NAME); + + // for compatiblity, start from here + private static final String LIST_CERTIFICATES = "listcerts"; private static final String LIST_USERKEYS = "listuserkeys"; private static final String PATH = "/data/misc/keystore/"; private static final String USERKEY_PATH = PATH + "userkeys/"; private static final String CERT_PATH = PATH + "certs/"; - private static final ServiceCommand mServiceCommand = - new ServiceCommand(SERVICE_NAME); @Override public String getUserkey(String key) { @@ -81,12 +122,6 @@ public abstract class Keystore { return CERT_PATH + key; } - /** - * Returns the array of the certificate names in keystore if successful. - * Or return an empty array if error. - * - * @return array of the certificates - */ @Override public String[] getAllCertificateKeys() { try { @@ -98,12 +133,6 @@ public abstract class Keystore { } } - /** - * Returns the array of the names of private keys in keystore if successful. - * Or return an empty array if errors. - * - * @return array of the user keys - */ @Override public String[] getAllUserkeyKeys() { try { @@ -115,6 +144,48 @@ public abstract class Keystore { } } + // to here + + @Override + public String getUserPrivateKey(String key) { + return mServiceCommand.execute( + GET_USER_KEY + COMMAND_DELIMITER + key); + } + + @Override + public String getUserCertificate(String key) { + return mServiceCommand.execute( + GET_USER_CERTIFICATE + COMMAND_DELIMITER + key); + } + + @Override + public String getCaCertificate(String key) { + return mServiceCommand.execute( + GET_CA_CERTIFICATE + COMMAND_DELIMITER + key); + } + + @Override + public String[] getAllUserCertificateKeys() { + try { + String result = mServiceCommand.execute(LIST_USER_CERTIFICATES); + if (result != null) return result.split("\\s+"); + return NOTFOUND; + } catch (NumberFormatException ex) { + return NOTFOUND; + } + } + + @Override + public String[] getAllCaCertificateKeys() { + try { + String result = mServiceCommand.execute(LIST_CA_CERTIFICATES); + if (result != null) return result.split("\\s+"); + return NOTFOUND; + } catch (NumberFormatException ex) { + return NOTFOUND; + } + } + @Override public String[] getSupportedKeyStrenghs() { // TODO: real implementation @@ -146,8 +217,29 @@ public abstract class Keystore { } @Override - public void addCertificate(String cert) { + public void addCertificate(byte[] cert) { // TODO: real implementation } + + private boolean addUserCertificate(String key, String certificate, + String privateKey) { + if(mServiceCommand.execute(ADD_USER_CERTIFICATE + COMMAND_DELIMITER + + key + COMMAND_DELIMITER + certificate) != null) { + if (mServiceCommand.execute(ADD_USER_KEY + COMMAND_DELIMITER + + key + COMMAND_DELIMITER + privateKey) != null) { + return true; + } + } + return false; + } + + private boolean addCaCertificate(String key, String content) { + if (mServiceCommand.execute(ADD_CA_CERTIFICATE + COMMAND_DELIMITER + + key + COMMAND_DELIMITER + content) != null) { + return true; + } + return false; + } + } } diff --git a/libs/rs/RenderScriptEnv.h b/libs/rs/RenderScriptEnv.h index 1bf5f22..221ab41 100644 --- a/libs/rs/RenderScriptEnv.h +++ b/libs/rs/RenderScriptEnv.h @@ -30,64 +30,64 @@ typedef struct { #define RS_PROGRAM_VERTEX_TEXTURE_OFFSET 32 typedef struct { - const void * (*loadEnvVp)(void *con, uint32_t bank, uint32_t offset); + const void * (*loadEnvVp)(uint32_t bank, uint32_t offset); - float (*loadEnvF)(void *con, uint32_t bank, uint32_t offset); - int32_t (*loadEnvI32)(void *con, uint32_t bank, uint32_t offset); - uint32_t (*loadEnvU32)(void *con, uint32_t bank, uint32_t offset); - void (*loadEnvVec4)(void *con, uint32_t bank, uint32_t offset, rsc_Vector4 *); - void (*loadEnvMatrix)(void *con, uint32_t bank, uint32_t offset, rsc_Matrix *); + float (*loadEnvF)(uint32_t bank, uint32_t offset); + int32_t (*loadEnvI32)(uint32_t bank, uint32_t offset); + uint32_t (*loadEnvU32)(uint32_t bank, uint32_t offset); + void (*loadEnvVec4)(uint32_t bank, uint32_t offset, rsc_Vector4 *); + void (*loadEnvMatrix)(uint32_t bank, uint32_t offset, rsc_Matrix *); - void (*storeEnvF)(void *con, uint32_t bank, uint32_t offset, float); - void (*storeEnvI32)(void *con, uint32_t bank, uint32_t offset, int32_t); - void (*storeEnvU32)(void *con, uint32_t bank, uint32_t offset, uint32_t); - void (*storeEnvVec4)(void *con, uint32_t bank, uint32_t offset, const rsc_Vector4 *); - void (*storeEnvMatrix)(void *con, uint32_t bank, uint32_t offset, const rsc_Matrix *); + void (*storeEnvF)(uint32_t bank, uint32_t offset, float); + void (*storeEnvI32)(uint32_t bank, uint32_t offset, int32_t); + void (*storeEnvU32)(uint32_t bank, uint32_t offset, uint32_t); + void (*storeEnvVec4)(uint32_t bank, uint32_t offset, const rsc_Vector4 *); + void (*storeEnvMatrix)(uint32_t bank, uint32_t offset, const rsc_Matrix *); - void (*matrixLoadIdentity)(void *con, rsc_Matrix *); - void (*matrixLoadFloat)(void *con, rsc_Matrix *, const float *); - void (*matrixLoadMat)(void *con, rsc_Matrix *, const rsc_Matrix *); - void (*matrixLoadRotate)(void *con, rsc_Matrix *, float rot, float x, float y, float z); - void (*matrixLoadScale)(void *con, rsc_Matrix *, float x, float y, float z); - void (*matrixLoadTranslate)(void *con, rsc_Matrix *, float x, float y, float z); - void (*matrixLoadMultiply)(void *con, rsc_Matrix *, const rsc_Matrix *lhs, const rsc_Matrix *rhs); - void (*matrixMultiply)(void *con, rsc_Matrix *, const rsc_Matrix *rhs); - void (*matrixRotate)(void *con, rsc_Matrix *, float rot, float x, float y, float z); - void (*matrixScale)(void *con, rsc_Matrix *, float x, float y, float z); - void (*matrixTranslate)(void *con, rsc_Matrix *, float x, float y, float z); + void (*matrixLoadIdentity)(rsc_Matrix *); + void (*matrixLoadFloat)(rsc_Matrix *, const float *); + void (*matrixLoadMat)(rsc_Matrix *, const rsc_Matrix *); + void (*matrixLoadRotate)(rsc_Matrix *, float rot, float x, float y, float z); + void (*matrixLoadScale)(rsc_Matrix *, float x, float y, float z); + void (*matrixLoadTranslate)(rsc_Matrix *, float x, float y, float z); + void (*matrixLoadMultiply)(rsc_Matrix *, const rsc_Matrix *lhs, const rsc_Matrix *rhs); + void (*matrixMultiply)(rsc_Matrix *, const rsc_Matrix *rhs); + void (*matrixRotate)(rsc_Matrix *, float rot, float x, float y, float z); + void (*matrixScale)(rsc_Matrix *, float x, float y, float z); + void (*matrixTranslate)(rsc_Matrix *, float x, float y, float z); - void (*color)(void *con, float r, float g, float b, float a); + void (*color)(float r, float g, float b, float a); - void (*programFragmentBindTexture)(void *con, RsProgramFragment, uint32_t slot, RsAllocation); - void (*programFragmentBindSampler)(void *con, RsProgramFragment, uint32_t slot, RsAllocation); + void (*programFragmentBindTexture)(RsProgramFragment, uint32_t slot, RsAllocation); + void (*programFragmentBindSampler)(RsProgramFragment, uint32_t slot, RsAllocation); - void (*materialDiffuse)(void *con, float r, float g, float b, float a); - void (*materialSpecular)(void *con, float r, float g, float b, float a); - void (*lightPosition)(void *con, float x, float y, float z, float w); - void (*materialShininess)(void *con, float s); + void (*materialDiffuse)(float r, float g, float b, float a); + void (*materialSpecular)(float r, float g, float b, float a); + void (*lightPosition)(float x, float y, float z, float w); + void (*materialShininess)(float s); - void (*uploadToTexture)(void *con, RsAllocation va, uint32_t baseMipLevel); + void (*uploadToTexture)(RsAllocation va, uint32_t baseMipLevel); - void (*enable)(void *con, uint32_t); - void (*disable)(void *con, uint32_t); + void (*enable)(uint32_t); + void (*disable)(uint32_t); - uint32_t (*rand)(void *con, uint32_t max); + uint32_t (*rand)(uint32_t max); - void (*contextBindProgramFragment)(void *con, RsProgramFragment pf); - void (*contextBindProgramFragmentStore)(void *con, RsProgramFragmentStore pfs); + void (*contextBindProgramFragment)(RsProgramFragment pf); + void (*contextBindProgramFragmentStore)(RsProgramFragmentStore pfs); // Drawing funcs - void (*renderTriangleMesh)(void *con, RsTriangleMesh); - void (*renderTriangleMeshRange)(void *con, RsTriangleMesh, uint32_t start, uint32_t count); + void (*renderTriangleMesh)(RsTriangleMesh); + void (*renderTriangleMeshRange)(RsTriangleMesh, uint32_t start, uint32_t count); // Assumes (GL_FIXED) x,y,z (GL_UNSIGNED_BYTE)r,g,b,a - void (*drawTriangleArray)(void *con, RsAllocation alloc, uint32_t count); + void (*drawTriangleArray)(RsAllocation alloc, uint32_t count); - void (*drawRect)(void *con, int32_t x1, int32_t x2, int32_t y1, int32_t y2); + void (*drawRect)(int32_t x1, int32_t x2, int32_t y1, int32_t y2); } rsc_FunctionTable; -typedef int (*rsc_RunScript)(void *con, const rsc_FunctionTable *, uint32_t launchID); +typedef int (*rsc_RunScript)(uint32_t launchIndex, const rsc_FunctionTable *); /* EnableCap */ diff --git a/libs/rs/java/Film/Android.mk b/libs/rs/java/Film/Android.mk new file mode 100644 index 0000000..2e9c243 --- /dev/null +++ b/libs/rs/java/Film/Android.mk @@ -0,0 +1,25 @@ +# +# Copyright (C) 2008 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. +# + +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := $(call all-java-files-under, src) +LOCAL_STATIC_JAVA_LIBRARIES := android.renderscript + +LOCAL_PACKAGE_NAME := Film + +include $(BUILD_PACKAGE) diff --git a/libs/rs/java/Film/AndroidManifest.xml b/libs/rs/java/Film/AndroidManifest.xml new file mode 100644 index 0000000..491ee14 --- /dev/null +++ b/libs/rs/java/Film/AndroidManifest.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="utf-8"?> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.film"> + <application android:label="Film"> + <activity android:name="Film" + android:theme="@android:style/Theme.Black.NoTitleBar"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + </application> +</manifest> diff --git a/libs/rs/java/Film/res/raw/filmimage.c b/libs/rs/java/Film/res/raw/filmimage.c new file mode 100644 index 0000000..3bd9496 --- /dev/null +++ b/libs/rs/java/Film/res/raw/filmimage.c @@ -0,0 +1,110 @@ +// Fountain test script + +#pragma version(1) +#pragma stateVertex(orthoWindow) +#pragma stateRaster(flat) +#pragma stateFragment(PgmFragBackground) +#pragma stateFragmentStore(MyBlend) + + +int main(void* con, int ft, int launchID) { + int count, touch, x, y, rate, maxLife, lifeShift; + int life; + int ct, ct2; + int newPart; + int drawCount; + int dx, dy, idx; + int posx,posy; + int c; + int srcIdx; + int dstIdx; + + count = loadI32(con, 0, 1); + touch = loadI32(con, 0, 2); + x = loadI32(con, 0, 3); + y = loadI32(con, 0, 4); + + rate = 4; + maxLife = (count / rate) - 1; + lifeShift = 0; + { + life = maxLife; + while (life > 255) { + life = life >> 1; + lifeShift ++; + } + } + + drawRect(con, 0, 256, 0, 512); + contextBindProgramFragment(con, NAMED_PgmFragParts); + + if (touch) { + newPart = loadI32(con, 2, 0); + for (ct2=0; ct2<rate; ct2++) { + dx = scriptRand(con, 0x10000) - 0x8000; + dy = scriptRand(con, 0x10000) - 0x8000; + + idx = newPart * 5 + 1; + storeI32(con, 2, idx, dx); + storeI32(con, 2, idx + 1, dy); + storeI32(con, 2, idx + 2, maxLife); + storeI32(con, 2, idx + 3, x << 16); + storeI32(con, 2, idx + 4, y << 16); + + newPart++; + if (newPart >= count) { + newPart = 0; + } + } + storeI32(con, 2, 0, newPart); + } + + drawCount = 0; + for (ct=0; ct < count; ct++) { + srcIdx = ct * 5 + 1; + + dx = loadI32(con, 2, srcIdx); + dy = loadI32(con, 2, srcIdx + 1); + life = loadI32(con, 2, srcIdx + 2); + posx = loadI32(con, 2, srcIdx + 3); + posy = loadI32(con, 2, srcIdx + 4); + + if (life) { + if (posy < (480 << 16)) { + dstIdx = drawCount * 9; + c = 0xffafcf | ((life >> lifeShift) << 24); + + storeU32(con, 1, dstIdx, c); + storeI32(con, 1, dstIdx + 1, posx); + storeI32(con, 1, dstIdx + 2, posy); + + storeU32(con, 1, dstIdx + 3, c); + storeI32(con, 1, dstIdx + 4, posx + 0x10000); + storeI32(con, 1, dstIdx + 5, posy + dy * 4); + + storeU32(con, 1, dstIdx + 6, c); + storeI32(con, 1, dstIdx + 7, posx - 0x10000); + storeI32(con, 1, dstIdx + 8, posy + dy * 4); + drawCount ++; + } else { + if (dy > 0) { + dy = (-dy) >> 1; + } + } + + posx = posx + dx; + posy = posy + dy; + dy = dy + 0x400; + life --; + + //storeI32(con, 2, srcIdx, dx); + storeI32(con, 2, srcIdx + 1, dy); + storeI32(con, 2, srcIdx + 2, life); + storeI32(con, 2, srcIdx + 3, posx); + storeI32(con, 2, srcIdx + 4, posy); + } + } + + drawTriangleArray(con, NAMED_PartBuffer, drawCount); + return 1; +} diff --git a/libs/rs/java/Film/res/raw/filmstrip.c b/libs/rs/java/Film/res/raw/filmstrip.c new file mode 100644 index 0000000..75810c0 --- /dev/null +++ b/libs/rs/java/Film/res/raw/filmstrip.c @@ -0,0 +1,131 @@ +// Fountain test script + +#pragma version(1) +#pragma stateVertex(PV) +#pragma stateFragment(default) +#pragma stateFragmentStore(default) + +/* +typedef struct FilmScriptUserEnvRec { + RsAllocation tex[13]; + int32_t triangleOffsets[64]; + float triangleOffsetsTex[64]; + int32_t triangleOffsetsCount; +} FilmScriptUserEnv; +*/ + +// The script enviroment has 3 env allocations. +// bank0: (r) The enviroment structure +// bank1: (r) The position information +// bank2: (rw) The temporary texture state + +int main(void* con, int ft, int index) +{ + int f1; + int f2; + int f3; + int f4; + int f5; + int f6; + int f7; + int f8; + int f9; + int f10; + int f11; + int f12; + int f13; + int f14; + int f15; + int f16; + + int trans; // float + int rot; // float + + + //trans = loadEnvF(con, 1, 0); + //rot = loadEnvF(con, 1, 1); + + //matrixLoadTranslate(con, &f1, 0, 0, trans); + //matrixRotate(con, &f1, rot, 1, 0, 0); + //matrixScale(con, &f1, 3.0f, 3.0f, 3.0f); + //storeEnvMatrix(con, 3, RS_PROGRAM_VERTEX_MODELVIEW_OFFSET, &f1); + + //rsc_Matrix m; + //int imgId = 0; + + // This should be replaced in the compiler with a + // smart load of a structure. + //const FilmScriptUserEnv *env = loadEnvVp(con, 0,0); + + //materialDiffuse(con, 0.0f, 0.0f, 0.0f, 1.0f); + //materialSpecular(con, 0.5f, 0.5f, 0.5f, 0.5f); + //materialShininess(con, 20.0f); + + + + //lightPosition(con, 0.2f, -0.2f, -2.0f, 0.0f); + + //contextBindProgramFragmentStore(con, NAMED_PFSBackground); + //contextBindProgramFragment(con, NAMED_PFBackground); + //enable(con, GL_LIGHTING); + renderTriangleMesh(con, NAMED_mesh); + +/* + contextBindProgramFragmentStore(con, env->fsImages); + contextBindProgramFragment(con, env->fpImages); + disable(con, GL_LIGHTING); + + float focusPos = loadEnvF(con, 1, 2); + int32_t focusID = 0; + int32_t lastFocusID = loadEnvI32(con, 2, 0); + int32_t imgCount = 13; + + if (trans > (-.3)) { + focusID = -1.0 - focusPos; + if (focusID >= imgCount) { + focusID = -1; + } + } else { + focusID = -1; + } + + if (focusID != lastFocusID) { + if (lastFocusID >= 0) { + uploadToTexture(con, env->tex[lastFocusID], 1); + } + if (focusID >= 0) { + uploadToTexture(con, env->tex[focusID], 0); + } + } + storeEnvI32(con, 2, 0, focusID); + + + for (imgId=1; imgId <= imgCount; imgId++) { + float pos = focusPos + imgId + .4f; + int offset = (int)floor(pos*2); + pos -= 0.75; + + offset += env->triangleOffsetsCount / 2; + + if ((offset < 0) || (offset >= env->triangleOffsetsCount)) { + continue; + } + + int start = offset -2; + int end = offset + 2; + + if (start < 0) { + start = 0; + } + if (end > env->triangleOffsetsCount) { + end = env->triangleOffsetsCount; + } + + programFragmentBindTexture(con, env->fpImages, 0, env->tex[imgId - 1]); + matrixLoadTranslate(con, &m, -pos - env->triangleOffsetsTex[env->triangleOffsetsCount / 2], 0, 0); + storeEnvMatrix(con, 3, RS_PROGRAM_VERTEX_TEXTURE_OFFSET, &m); + renderTriangleMeshRange(con, env->mesh, env->triangleOffsets[start], env->triangleOffsets[end] - env->triangleOffsets[start]); + } +*/ +} + diff --git a/libs/rs/java/Film/src/com/android/film/Film.java b/libs/rs/java/Film/src/com/android/film/Film.java new file mode 100644 index 0000000..6e99816 --- /dev/null +++ b/libs/rs/java/Film/src/com/android/film/Film.java @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2008 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.film; + +import android.renderscript.RSSurfaceView; +import android.renderscript.RenderScript; + +import android.app.Activity; +import android.content.res.Configuration; +import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.provider.Settings.System; +import android.util.Config; +import android.util.Log; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.view.Window; +import android.widget.Button; +import android.widget.ListView; + +import java.lang.Runtime; + +public class Film extends Activity { + //EventListener mListener = new EventListener(); + + private static final String LOG_TAG = "libRS_jni"; + private static final boolean DEBUG = false; + private static final boolean LOG_ENABLED = DEBUG ? Config.LOGD : Config.LOGV; + + private FilmView mView; + + // get the current looper (from your Activity UI thread for instance + + + + @Override + public void onCreate(Bundle icicle) { + super.onCreate(icicle); + + // Create our Preview view and set it as the content of our + // Activity + mView = new FilmView(this); + setContentView(mView); + } + + @Override + protected void onResume() { + // Ideally a game should implement onResume() and onPause() + // to take appropriate action when the activity looses focus + super.onResume(); + mView.onResume(); + } + + @Override + protected void onPause() { + // Ideally a game should implement onResume() and onPause() + // to take appropriate action when the activity looses focus + super.onPause(); + mView.onPause(); + + Runtime.getRuntime().exit(0); + } + + + static void log(String message) { + if (LOG_ENABLED) { + Log.v(LOG_TAG, message); + } + } + + +} + diff --git a/libs/rs/java/Film/src/com/android/film/FilmRS.java b/libs/rs/java/Film/src/com/android/film/FilmRS.java new file mode 100644 index 0000000..b4b5f4b --- /dev/null +++ b/libs/rs/java/Film/src/com/android/film/FilmRS.java @@ -0,0 +1,242 @@ +/* + * Copyright (C) 2008 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.film; + +import java.io.Writer; + +import android.renderscript.RenderScript; +import android.renderscript.ProgramVertexAlloc; +import android.renderscript.Matrix; + +import android.content.Context; +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.os.Handler; +import android.os.Message; +import android.util.AttributeSet; +import android.util.Log; +import android.view.Surface; +import android.view.SurfaceHolder; +import android.view.SurfaceView; +import android.view.KeyEvent; +import android.view.MotionEvent; + +public class FilmRS { + + public FilmRS() { + } + + public void init(RenderScript rs, Resources res, int width, int height) { + mRS = rs; + mRes = res; + initNamed(); + initRS(); + } + + public void setFilmStripPosition(int x, int y) + { + if (x < 0) { + x = 0; + } + if (x > 50) { + x = 50; + } + + float anim = ((float)x) / 50.f; + mBufferPos[0] = -2f * anim - .2f; // translation + mBufferPos[1] = -90 + (anim * 40); // rotation + mBufferPos[2] = ((float)y) / 16.f - 8; // focusPos + + mAllocPos.data(mBufferPos); + } + + + private Resources mRes; + private RenderScript mRS; + private RenderScript.Script mScriptStrip; + private RenderScript.Script mScriptImage; + private RenderScript.Element mElementVertex; + private RenderScript.Element mElementIndex; + private RenderScript.Sampler mSampler; + private RenderScript.ProgramFragmentStore mPFSBackground; + private RenderScript.ProgramFragmentStore mPFSImages; + private RenderScript.ProgramFragment mPFBackground; + private RenderScript.ProgramFragment mPFImages; + private RenderScript.ProgramVertex mPV; + private ProgramVertexAlloc mPVA; + + private RenderScript.Allocation mAllocEnv; + private RenderScript.Allocation mAllocPos; + private RenderScript.Allocation mAllocState; + private RenderScript.Allocation mAllocPV; + private RenderScript.TriangleMesh mMesh; + + private float[] mBufferPos; + private float[] mBufferPV; + + private void initNamed() { + mElementVertex = mRS.elementGetPredefined( + RenderScript.ElementPredefined.NORM_ST_XYZ_F32); + mElementIndex = mRS.elementGetPredefined( + RenderScript.ElementPredefined.INDEX_16); + + mRS.triangleMeshBegin(mElementVertex, mElementIndex); + FilmStripMesh fsm = new FilmStripMesh(); + fsm.init(mRS); + mMesh = mRS.triangleMeshCreate(); + mMesh.setName("mesh"); + Log.e("rs", "Done loading strips"); + + + mRS.samplerBegin(); + mRS.samplerSet(RenderScript.SamplerParam.FILTER_MIN, + RenderScript.SamplerValue.LINEAR_MIP_LINEAR); + mRS.samplerSet(RenderScript.SamplerParam.WRAP_MODE_S, + RenderScript.SamplerValue.CLAMP); + mRS.samplerSet(RenderScript.SamplerParam.WRAP_MODE_T, + RenderScript.SamplerValue.CLAMP); + mSampler = mRS.samplerCreate(); + + mRS.programFragmentBegin(null, null); + mPFBackground = mRS.programFragmentCreate(); + mPFBackground.setName("PFBackground"); + + mRS.programFragmentBegin(null, null); + mRS.programFragmentSetTexEnable(0, true); + //mRS.programFragmentSetEnvMode(0, RS_TEX_ENV_MODE_REPLACE); + //rsProgramFragmentSetType(0, gEnv.tex[0]->getType()); + mPFImages = mRS.programFragmentCreate(); + mPFImages.setName("PFImages"); + mPFImages.bindSampler(mSampler, 0); + + mRS.programFragmentStoreBegin(null, null); + mRS.programFragmentStoreDepthFunc(RenderScript.DepthFunc.LESS); + mRS.programFragmentStoreDitherEnable(true); + mPFSBackground = mRS.programFragmentStoreCreate(); + mPFSBackground.setName("PFSBackground"); + + mRS.programFragmentStoreBegin(null, null); + mRS.programFragmentStoreDepthFunc(RenderScript.DepthFunc.EQUAL); + mRS.programFragmentStoreDitherEnable(false); + mRS.programFragmentStoreDepthMask(false); + mRS.programFragmentStoreBlendFunc(RenderScript.BlendSrcFunc.ONE, + RenderScript.BlendDstFunc.ONE); + mPFSImages = mRS.programFragmentStoreCreate(); + mPFSImages.setName("PFSImages"); + + mRS.programVertexBegin(null, null); + mRS.programVertexSetTextureMatrixEnable(true); + mPV = mRS.programVertexCreate(); + mPV.setName("PV"); + + Log.e("rs", "Done loading named"); + } + + + private Bitmap mBackground; + + int mParams[] = new int[10]; + + private void initRS() { + int partCount = 1024; + + mRS.scriptCBegin(); + mRS.scriptCSetClearColor(0.0f, 0.0f, 0.0f, 1.0f); + mRS.scriptCSetScript(mRes, R.raw.filmstrip); + mRS.scriptCSetRoot(true); + mScriptStrip = mRS.scriptCCreate(); + + mBufferPos = new float[3]; + mAllocPos = mRS.allocationCreatePredefSized( + RenderScript.ElementPredefined.USER_FLOAT, + mBufferPos.length); + setFilmStripPosition(0, 0); + + mPVA = new ProgramVertexAlloc(mRS); + mPV.bindAllocation(0, mPVA.mAlloc); + mPVA.setupProjectionNormalized(320, 480); + + Matrix m = new Matrix(); + + m.loadIdentity(); + + m.translate(0, 0, 1); + m.rotate(90, 0, 0, 1); + m.rotate(20, 1, 0, 0); + mPVA.loadModelview(m); + + + + + + //mScriptStrip.bindAllocation(mEnvAlloc, 0); + mScriptStrip.bindAllocation(mAllocPos, 1); + //mScriptStrip.bindAllocation(gStateAlloc, 2); + mScriptStrip.bindAllocation(mPVA.mAlloc, 3); + + + //mIntAlloc = mRS.allocationCreatePredefSized(RenderScript.ElementPredefined.USER_I32, 10); + //mPartAlloc = mRS.allocationCreatePredefSized(RenderScript.ElementPredefined.USER_I32, partCount * 3 * 3); + //mPartAlloc.setName("PartBuffer"); + //mVertAlloc = mRS.allocationCreatePredefSized(RenderScript.ElementPredefined.USER_I32, partCount * 5 + 1); +/* + { + Resources res = getResources(); + Drawable d = res.getDrawable(R.drawable.gadgets_clock_mp3); + BitmapDrawable bd = (BitmapDrawable)d; + Bitmap b = bd.getBitmap(); + mTexture = mRS.allocationCreateFromBitmap(b, + RenderScript.ElementPredefined.RGB_565, + true); + mTexture.uploadToTexture(0); + } + + mRS.programFragmentStoreBegin(null, null); + mRS.programFragmentStoreBlendFunc(RenderScript.BlendSrcFunc.SRC_ALPHA, RenderScript.BlendDstFunc.ONE); + mRS.programFragmentStoreDepthFunc(RenderScript.DepthFunc.ALWAYS); + mPFS = mRS.programFragmentStoreCreate(); + mPFS.setName("MyBlend"); + mRS.contextBindProgramFragmentStore(mPFS); + + mRS.samplerBegin(); + mRS.samplerSet(RenderScript.SamplerParam.FILTER_MAG, RenderScript.SamplerValue.LINEAR); + mRS.samplerSet(RenderScript.SamplerParam.FILTER_MIN, RenderScript.SamplerValue.LINEAR); + mSampler = mRS.samplerCreate(); + + + mParams[0] = 0; + mParams[1] = partCount; + mParams[2] = 0; + mParams[3] = 0; + mParams[4] = 0; + mIntAlloc.data(mParams); + + int t2[] = new int[partCount * 4*3]; + for (int ct=0; ct < t2.length; ct++) { + t2[ct] = 0; + } + mPartAlloc.data(t2); + */ + + mRS.contextBindRootScript(mScriptStrip); + } +} + + + diff --git a/libs/rs/java/Film/src/com/android/film/FilmStripMesh.java b/libs/rs/java/Film/src/com/android/film/FilmStripMesh.java new file mode 100644 index 0000000..02bffd8 --- /dev/null +++ b/libs/rs/java/Film/src/com/android/film/FilmStripMesh.java @@ -0,0 +1,255 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +package com.android.film; + +import java.io.Writer; +import java.lang.Math; +import android.util.Log; + +import android.renderscript.RenderScript; + + +class FilmStripMesh { + + class Vertex { + float nx; + float ny; + float nz; + float s; + float t; + float x; + float y; + float z; + + Vertex() { + nx = 0; + ny = 0; + nz = 0; + s = 0; + t = 0; + x = 0; + y = 0; + z = 0; + } + + void xyz(float _x, float _y, float _z) { + x = _x; + y = _y; + z = _z; + } + + void nxyz(float _x, float _y, float _z) { + nx = _x; + ny = _y; + nz = _z; + } + + void st(float _s, float _t) { + s = _s; + t = _t; + } + + void computeNorm(Vertex v1, Vertex v2) { + float dx = v1.x - v2.x; + float dy = v1.y - v2.y; + float dz = v1.z - v2.z; + float len = (float)java.lang.Math.sqrt(dx*dx + dy*dy + dz*dz); + dx /= len; + dy /= len; + dz /= len; + + nx = dx * dz; + ny = dy * dz; + nz = (float)java.lang.Math.sqrt(dx*dx + dy*dy); + + len = (float)java.lang.Math.sqrt(nx*nx + ny*ny + nz*nz); + nx /= len; + ny /= len; + nz /= len; + } + + void addToRS(RenderScript rs) { + rs.triangleMeshAddVertex_XYZ_ST_NORM(x, y, z, s, t, nx, ny, nz); + } + } + + int[] mTriangleOffsets; + float[] mTriangleOffsetsTex; + int mTriangleOffsetsCount; + + void init(RenderScript rs) + { + float vtx[] = new float[] { + 60.431003f, 124.482050f, + 60.862074f, 120.872604f, + 61.705303f, 117.336662f, + 62.949505f, 113.921127f, + 64.578177f, 110.671304f, + 66.569716f, 107.630302f, + 68.897703f, 104.838457f, + 71.531259f, 102.332803f, + 74.435452f, 100.146577f, + 77.571757f, 98.308777f, + 80.898574f, 96.843781f, + 84.371773f, 95.771023f, + 87.945283f, 95.104731f, + 98.958994f, 95.267098f, + 109.489523f, 98.497596f, + 118.699582f, 104.539366f, + 125.856872f, 112.912022f, + 130.392311f, 122.949849f, + 131.945283f, 133.854731f, + 130.392311f, 144.759613f, + 125.856872f, 154.797439f, + 118.699582f, 163.170096f, + 109.489523f, 169.211866f, + 98.958994f, 172.442364f, + 87.945283f, 172.604731f, + 72.507313f, 172.672927f, + 57.678920f, 168.377071f, + 44.668135f, 160.067134f, + 34.534908f, 148.420104f, + 28.104767f, 134.384831f, + 25.901557f, 119.104731f, + 28.104767f, 103.824631f, + 34.534908f, 89.789358f, + 44.668135f, 78.142327f, + 57.678920f, 69.832390f, + 72.507313f, 65.536534f, + 87.945283f, 65.604731f, + 106.918117f, 65.688542f, + 125.141795f, 60.409056f, + 141.131686f, 50.196376f, + 153.585137f, 35.882502f, + 161.487600f, 18.633545f, + 164.195283f, -0.145269f, + 161.487600f, -18.924084f, + 153.585137f, -36.173040f, + 141.131686f, -50.486914f, + 125.141795f, -60.699594f, + 106.918117f, -65.979081f, + 87.945283f, -65.895269f, + 80f, -65.895269f, + 60f, -65.895269f, + 40f, -65.895269f, + 20f, -65.895269f, + 0f, -65.895269f, + -20f, -65.895269f, + -40f, -65.895269f, + -60f, -65.895269f, + -80f, -65.895269f, + -87.945283f, -65.895269f, + -106.918117f, -65.979081f, + -125.141795f, -60.699594f, + -141.131686f, -50.486914f, + -153.585137f, -36.173040f, + -161.487600f, -18.924084f, + -164.195283f, -0.145269f, + -161.487600f, 18.633545f, + -153.585137f, 35.882502f, + -141.131686f, 50.196376f, + -125.141795f, 60.409056f, + -106.918117f, 65.688542f, + -87.945283f, 65.604731f, + -72.507313f, 65.536534f, + -57.678920f, 69.832390f, + -44.668135f, 78.142327f, + -34.534908f, 89.789358f, + -28.104767f, 103.824631f, + -25.901557f, 119.104731f, + -28.104767f, 134.384831f, + -34.534908f, 148.420104f, + -44.668135f, 160.067134f, + -57.678920f, 168.377071f, + -72.507313f, 172.672927f, + -87.945283f, 172.604731f, + -98.958994f, 172.442364f, + -109.489523f, 169.211866f, + -118.699582f, 163.170096f, + -125.856872f, 154.797439f, + -130.392311f, 144.759613f, + -131.945283f, 133.854731f, + -130.392311f, 122.949849f, + -125.856872f, 112.912022f, + -118.699582f, 104.539366f, + -109.489523f, 98.497596f, + -98.958994f, 95.267098f, + -87.945283f, 95.104731f, + -84.371773f, 95.771023f, + -80.898574f, 96.843781f, + -77.571757f, 98.308777f, + -74.435452f, 100.146577f, + -71.531259f, 102.332803f, + -68.897703f, 104.838457f, + -66.569716f, 107.630302f, + -64.578177f, 110.671304f, + -62.949505f, 113.921127f, + -61.705303f, 117.336662f, + -60.862074f, 120.872604f, + -60.431003f, 124.482050f + }; + + + mTriangleOffsets = new int[64]; + mTriangleOffsetsTex = new float[64]; + + mTriangleOffsets[0] = 0; + mTriangleOffsetsCount = 1; + + Vertex t = new Vertex(); + t.nxyz(1, 0, 0); + int count = vtx.length / 2; + + float runningS = 0; + for (int ct=0; ct < (count-1); ct++) { + t.x = -vtx[ct*2] / 100.f; + t.z = vtx[ct*2+1] / 100.f; + t.s = runningS; + t.nx = (vtx[ct*2+3] - vtx[ct*2 +1]); + t.ny = (vtx[ct*2+2] - vtx[ct*2 ]); + float len = (float)java.lang.Math.sqrt(t.nx * t.nx + t.ny * t.ny); + runningS += len / 100; + t.nx /= len; + t.ny /= len; + t.y = -0.5f; + t.t = 0; + //Log.e("xx", "vtx " + t.x + " " + t.y + " " + t.z); + t.addToRS(rs); + t.y = .5f; + t.t = 1; + t.addToRS(rs); + + //LOGE(" %f", runningS); + if((runningS*2) > mTriangleOffsetsCount) { + //LOGE("**** img %i %i", gTriangleOffsetsCount, ct*2); + mTriangleOffsets[mTriangleOffsetsCount] = ct*2; + mTriangleOffsetsTex[mTriangleOffsetsCount] = t.s; + mTriangleOffsetsCount ++; + } + } + + count = (count * 2 - 2); + for (int ct=0; ct < (count-2); ct+= 2) { + rs.triangleMeshAddTriangle(ct, ct+1, ct+2); + rs.triangleMeshAddTriangle(ct+1, ct+3, ct+2); + } + } + + +} + diff --git a/libs/rs/java/Film/src/com/android/film/FilmView.java b/libs/rs/java/Film/src/com/android/film/FilmView.java new file mode 100644 index 0000000..452d522 --- /dev/null +++ b/libs/rs/java/Film/src/com/android/film/FilmView.java @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2008 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.film; + +import java.io.Writer; +import java.util.ArrayList; +import java.util.concurrent.Semaphore; + +import android.renderscript.RSSurfaceView; +import android.renderscript.RenderScript; + +import android.content.Context; +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.os.Handler; +import android.os.Message; +import android.util.AttributeSet; +import android.util.Log; +import android.view.Surface; +import android.view.SurfaceHolder; +import android.view.SurfaceView; +import android.view.KeyEvent; +import android.view.MotionEvent; + +public class FilmView extends RSSurfaceView { + + public FilmView(Context context) { + super(context); + + //setFocusable(true); + } + + private RenderScript mRS; + private FilmRS mRender; + + public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) { + super.surfaceChanged(holder, format, w, h); + + mRS = createRenderScript(); + mRender = new FilmRS(); + mRender.init(mRS, getResources(), w, h); + } + + @Override + public boolean onKeyDown(int keyCode, KeyEvent event) + { + // break point at here + // this method doesn't work when 'extends View' include 'extends ScrollView'. + return super.onKeyDown(keyCode, event); + } + + + @Override + public boolean onTouchEvent(MotionEvent ev) + { + boolean ret = true; + int act = ev.getAction(); + if (act == ev.ACTION_UP) { + ret = false; + } + //mRender.newTouchPosition((int)ev.getX(), (int)ev.getY()); + return ret; + } +} + + diff --git a/libs/rs/java/Fountain/res/raw/fountain.c b/libs/rs/java/Fountain/res/raw/fountain.c index 3bd9496..8a55406 100644 --- a/libs/rs/java/Fountain/res/raw/fountain.c +++ b/libs/rs/java/Fountain/res/raw/fountain.c @@ -1,13 +1,12 @@ // Fountain test script #pragma version(1) -#pragma stateVertex(orthoWindow) -#pragma stateRaster(flat) +#pragma stateVertex(default) #pragma stateFragment(PgmFragBackground) -#pragma stateFragmentStore(MyBlend) +#pragma stateFragmentStore(parent) -int main(void* con, int ft, int launchID) { +int main(int launchID) { int count, touch, x, y, rate, maxLife, lifeShift; int life; int ct, ct2; @@ -19,10 +18,10 @@ int main(void* con, int ft, int launchID) { int srcIdx; int dstIdx; - count = loadI32(con, 0, 1); - touch = loadI32(con, 0, 2); - x = loadI32(con, 0, 3); - y = loadI32(con, 0, 4); + count = loadI32(0, 1); + touch = loadI32(0, 2); + x = loadI32(0, 3); + y = loadI32(0, 4); rate = 4; maxLife = (count / rate) - 1; @@ -35,56 +34,56 @@ int main(void* con, int ft, int launchID) { } } - drawRect(con, 0, 256, 0, 512); - contextBindProgramFragment(con, NAMED_PgmFragParts); + drawRect(0, 256, 0, 512); + contextBindProgramFragment(NAMED_PgmFragParts); if (touch) { - newPart = loadI32(con, 2, 0); + newPart = loadI32(2, 0); for (ct2=0; ct2<rate; ct2++) { - dx = scriptRand(con, 0x10000) - 0x8000; - dy = scriptRand(con, 0x10000) - 0x8000; + dx = scriptRand(0x10000) - 0x8000; + dy = scriptRand(0x10000) - 0x8000; idx = newPart * 5 + 1; - storeI32(con, 2, idx, dx); - storeI32(con, 2, idx + 1, dy); - storeI32(con, 2, idx + 2, maxLife); - storeI32(con, 2, idx + 3, x << 16); - storeI32(con, 2, idx + 4, y << 16); + storeI32(2, idx, dx); + storeI32(2, idx + 1, dy); + storeI32(2, idx + 2, maxLife); + storeI32(2, idx + 3, x << 16); + storeI32(2, idx + 4, y << 16); newPart++; if (newPart >= count) { newPart = 0; } } - storeI32(con, 2, 0, newPart); + storeI32(2, 0, newPart); } drawCount = 0; for (ct=0; ct < count; ct++) { srcIdx = ct * 5 + 1; - dx = loadI32(con, 2, srcIdx); - dy = loadI32(con, 2, srcIdx + 1); - life = loadI32(con, 2, srcIdx + 2); - posx = loadI32(con, 2, srcIdx + 3); - posy = loadI32(con, 2, srcIdx + 4); + dx = loadI32(2, srcIdx); + dy = loadI32(2, srcIdx + 1); + life = loadI32(2, srcIdx + 2); + posx = loadI32(2, srcIdx + 3); + posy = loadI32(2, srcIdx + 4); if (life) { if (posy < (480 << 16)) { dstIdx = drawCount * 9; c = 0xffafcf | ((life >> lifeShift) << 24); - storeU32(con, 1, dstIdx, c); - storeI32(con, 1, dstIdx + 1, posx); - storeI32(con, 1, dstIdx + 2, posy); + storeU32(1, dstIdx, c); + storeI32(1, dstIdx + 1, posx); + storeI32(1, dstIdx + 2, posy); - storeU32(con, 1, dstIdx + 3, c); - storeI32(con, 1, dstIdx + 4, posx + 0x10000); - storeI32(con, 1, dstIdx + 5, posy + dy * 4); + storeU32(1, dstIdx + 3, c); + storeI32(1, dstIdx + 4, posx + 0x10000); + storeI32(1, dstIdx + 5, posy + dy * 4); - storeU32(con, 1, dstIdx + 6, c); - storeI32(con, 1, dstIdx + 7, posx - 0x10000); - storeI32(con, 1, dstIdx + 8, posy + dy * 4); + storeU32(1, dstIdx + 6, c); + storeI32(1, dstIdx + 7, posx - 0x10000); + storeI32(1, dstIdx + 8, posy + dy * 4); drawCount ++; } else { if (dy > 0) { @@ -97,14 +96,14 @@ int main(void* con, int ft, int launchID) { dy = dy + 0x400; life --; - //storeI32(con, 2, srcIdx, dx); - storeI32(con, 2, srcIdx + 1, dy); - storeI32(con, 2, srcIdx + 2, life); - storeI32(con, 2, srcIdx + 3, posx); - storeI32(con, 2, srcIdx + 4, posy); + //storeI32(2, srcIdx, dx); + storeI32(2, srcIdx + 1, dy); + storeI32(2, srcIdx + 2, life); + storeI32(2, srcIdx + 3, posx); + storeI32(2, srcIdx + 4, posy); } } - drawTriangleArray(con, NAMED_PartBuffer, drawCount); + drawTriangleArray(NAMED_PartBuffer, drawCount); return 1; } diff --git a/libs/rs/java/Fountain/src/com/android/fountain/FountainRS.java b/libs/rs/java/Fountain/src/com/android/fountain/FountainRS.java index 0a0b05a..467115a 100644 --- a/libs/rs/java/Fountain/src/com/android/fountain/FountainRS.java +++ b/libs/rs/java/Fountain/src/com/android/fountain/FountainRS.java @@ -18,12 +18,13 @@ package com.android.fountain; import java.io.Writer; -import android.renderscript.RenderScript; import android.content.Context; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; +import android.renderscript.RenderScript; +import android.renderscript.ProgramVertexAlloc; import android.util.Log; public class FountainRS { @@ -107,6 +108,7 @@ public class FountainRS { mPF2.bindSampler(mSampler, 0); mPF2.setName("PgmFragBackground"); + mParams[0] = 0; mParams[1] = partCount; mParams[2] = 0; diff --git a/libs/rs/java/RenderScript/android/renderscript/Matrix.java b/libs/rs/java/RenderScript/android/renderscript/Matrix.java index 03222aa..79b60d0 100644 --- a/libs/rs/java/RenderScript/android/renderscript/Matrix.java +++ b/libs/rs/java/RenderScript/android/renderscript/Matrix.java @@ -20,7 +20,7 @@ import java.lang.Math; import android.util.Log; -class Matrix { +public class Matrix { public Matrix() { mMat = new float[16]; @@ -139,8 +139,8 @@ class Matrix { mMat[5] = 2 / (t - b); mMat[10]= -2 / (f - n); mMat[12]= -(r + l) / (r - l); - mMat[12]= -(t + b) / (t - b); - mMat[12]= -(f + n) / (f - n); + mMat[13]= -(t + b) / (t - b); + mMat[14]= -(f + n) / (f - n); } public void loadFrustum(float l, float r, float b, float t, float n, float f) { diff --git a/libs/rs/java/RenderScript/android/renderscript/RenderScript.java b/libs/rs/java/RenderScript/android/renderscript/RenderScript.java index afb4ae3..4d5c4a0 100644 --- a/libs/rs/java/RenderScript/android/renderscript/RenderScript.java +++ b/libs/rs/java/RenderScript/android/renderscript/RenderScript.java @@ -154,10 +154,7 @@ public class RenderScript { native private void nProgramVertexBindAllocation(int pv, int slot, int mID); native private void nProgramVertexBegin(int inID, int outID); native private void nProgramVertexSetType(int slot, int mID); - native private void nProgramVertexSetCameraMode(boolean isOrtho); native private void nProgramVertexSetTextureMatrixEnable(boolean enable); - native private void nProgramVertexSetModelMatrixEnable(boolean enable); - native private void nProgramVertexSetProjectionMatrixEnable(boolean enable); native private int nProgramVertexCreate(); @@ -733,22 +730,10 @@ public class RenderScript { nProgramVertexSetType(slot, t.mID); } - public void programVertexSetCameraMode(boolean isOrtho) { - nProgramVertexSetCameraMode(isOrtho); - } - public void programVertexSetTextureMatrixEnable(boolean enable) { nProgramVertexSetTextureMatrixEnable(enable); } - public void programVertexSetModelMatrixEnable(boolean enable) { - nProgramVertexSetModelMatrixEnable(enable); - } - - public void programVertexSetProjectionMatrixEnable(boolean enable) { - nProgramVertexSetProjectionMatrixEnable(enable); - } - public ProgramVertex programVertexCreate() { int id = nProgramVertexCreate(); return new ProgramVertex(id); diff --git a/libs/rs/java/Rollo/res/raw/rollo.c b/libs/rs/java/Rollo/res/raw/rollo.c index b81c567..56ee425 100644 --- a/libs/rs/java/Rollo/res/raw/rollo.c +++ b/libs/rs/java/Rollo/res/raw/rollo.c @@ -8,6 +8,7 @@ int main(void* con, int ft, int launchID) int x; renderTriangleMesh(con, NAMED_MeshCard); + renderTriangleMesh(con, NAMED_MeshTab); return 1; } diff --git a/libs/rs/java/Rollo/src/com/android/rollo/RolloMesh.java b/libs/rs/java/Rollo/src/com/android/rollo/RolloMesh.java index c44a817..d7252fb 100644 --- a/libs/rs/java/Rollo/src/com/android/rollo/RolloMesh.java +++ b/libs/rs/java/Rollo/src/com/android/rollo/RolloMesh.java @@ -24,6 +24,11 @@ import android.renderscript.RenderScript; class RolloMesh { + static public final float mCardHeight = 1.2f; + static public final float mCardWidth = 1.8f; + static public final float mTabHeight = 0.2f; + static public final float mTabs = 3; + static public final float mTabGap = 0.1f; static RenderScript.TriangleMesh createCard(RenderScript rs) { RenderScript.Element vtx = rs.elementGetPredefined( @@ -31,12 +36,15 @@ class RolloMesh { RenderScript.Element idx = rs.elementGetPredefined( RenderScript.ElementPredefined.INDEX_16); - rs.triangleMeshBegin(vtx, idx); - rs.triangleMeshAddVertex_XYZ_ST(0, 0, 0, 0, 0); - rs.triangleMeshAddVertex_XYZ_ST(0, 1, 0, 0, 1); - rs.triangleMeshAddVertex_XYZ_ST(1, 1, 0, 1, 1); - rs.triangleMeshAddVertex_XYZ_ST(1, 0, 0, 1, 0); + float w = mCardWidth / 2; + float h = mCardHeight; + float z = 0; + rs.triangleMeshBegin(vtx, idx); + rs.triangleMeshAddVertex_XYZ_ST(-w, 0, z, 0, 0); + rs.triangleMeshAddVertex_XYZ_ST(-w, h, z, 0, 1); + rs.triangleMeshAddVertex_XYZ_ST( w, h, z, 1, 1); + rs.triangleMeshAddVertex_XYZ_ST( w, 0, z, 1, 0); rs.triangleMeshAddTriangle(0,1,2); rs.triangleMeshAddTriangle(0,2,3); return rs.triangleMeshCreate(); @@ -48,11 +56,28 @@ class RolloMesh { RenderScript.Element idx = rs.elementGetPredefined( RenderScript.ElementPredefined.INDEX_16); + + float tabSlope = 0.1f; + float num = 0; + + float w = (mCardWidth - ((mTabs - 1) * mTabGap)) / mTabs; + float w1 = -(mCardWidth / 2) + ((w + mTabGap) * num); + float w2 = w1 + (w * tabSlope); + float w3 = w1 + w - (w * tabSlope); + float w4 = w1 + w; + float h1 = mCardHeight; + float h2 = h1 + mTabHeight; + float z = 0; + + float stScale = w / mTabHeight / 2; + float stScale2 = stScale * (tabSlope / w); + + rs.triangleMeshBegin(vtx, idx); - rs.triangleMeshAddVertex_XYZ_ST(0.0f, 0, 0, -1.0f, 0); - rs.triangleMeshAddVertex_XYZ_ST(0.2f, 1, 0, -0.8f, 1); - rs.triangleMeshAddVertex_XYZ_ST(1.8f, 1, 0, 0.8f, 1); - rs.triangleMeshAddVertex_XYZ_ST(2.0f, 0, 0, 1.0f, 0); + rs.triangleMeshAddVertex_XYZ_ST(w1, h1, z, -stScale, 0); + rs.triangleMeshAddVertex_XYZ_ST(w2, h2, z, -stScale2, 1); + rs.triangleMeshAddVertex_XYZ_ST(w3, h2, z, stScale2, 1); + rs.triangleMeshAddVertex_XYZ_ST(w4, h1, z, stScale, 0); rs.triangleMeshAddTriangle(0,1,2); rs.triangleMeshAddTriangle(0,2,3); return rs.triangleMeshCreate(); diff --git a/libs/rs/java/Rollo/src/com/android/rollo/RolloRS.java b/libs/rs/java/Rollo/src/com/android/rollo/RolloRS.java index aa9f338..da0b146 100644 --- a/libs/rs/java/Rollo/src/com/android/rollo/RolloRS.java +++ b/libs/rs/java/Rollo/src/com/android/rollo/RolloRS.java @@ -74,8 +74,8 @@ public class RolloRS { //private float[] mBufferPV; private void initNamed() { - //mMeshTab = RolloMesh.createTab(mRS); - //mMeshTab.setName("MeshTab"); + mMeshTab = RolloMesh.createTab(mRS); + mMeshTab.setName("MeshTab"); mMeshCard = RolloMesh.createCard(mRS); mMeshCard.setName("MeshCard"); Log.e("rs", "Done loading strips"); @@ -117,10 +117,7 @@ public class RolloRS { mPVAlloc = new ProgramVertexAlloc(mRS); mRS.programVertexBegin(null, null); - mRS.programVertexSetCameraMode(true); mRS.programVertexSetTextureMatrixEnable(true); - mRS.programVertexSetModelMatrixEnable(true); - mRS.programVertexSetProjectionMatrixEnable(true); mPV = mRS.programVertexCreate(); mPV.setName("PV"); mPV.bindAllocation(0, mPVAlloc.mAlloc); diff --git a/libs/rs/jni/RenderScript_jni.cpp b/libs/rs/jni/RenderScript_jni.cpp index 7a3a7af..ab17a4a 100644 --- a/libs/rs/jni/RenderScript_jni.cpp +++ b/libs/rs/jni/RenderScript_jni.cpp @@ -791,14 +791,6 @@ nProgramVertexSetType(JNIEnv *_env, jobject _this, jint slot, jint t) } static void -nProgramVertexSetCameraMode(JNIEnv *_env, jobject _this, jboolean isOrtho) -{ - RsContext con = (RsContext)(_env->GetIntField(_this, gContextId)); - LOG_API("nProgramVertexSetCameraMode, con(%p), isOrtho(%i)", con, isOrtho); - rsProgramVertexSetCameraMode(isOrtho); -} - -static void nProgramVertexSetTextureMatrixEnable(JNIEnv *_env, jobject _this, jboolean enable) { RsContext con = (RsContext)(_env->GetIntField(_this, gContextId)); @@ -806,22 +798,6 @@ nProgramVertexSetTextureMatrixEnable(JNIEnv *_env, jobject _this, jboolean enabl rsProgramVertexSetTextureMatrixEnable(enable); } -static void -nProgramVertexSetModelMatrixEnable(JNIEnv *_env, jobject _this, jboolean enable) -{ - RsContext con = (RsContext)(_env->GetIntField(_this, gContextId)); - LOG_API("nProgramVertexSetModelMatrixEnable, con(%p), enable(%i)", con, enable); - rsProgramVertexSetModelMatrixEnable(enable); -} - -static void -nProgramVertexSetProjectionMatrixEnable(JNIEnv *_env, jobject _this, jboolean enable) -{ - RsContext con = (RsContext)(_env->GetIntField(_this, gContextId)); - LOG_API("nProgramVertexSetProjectionMatrixEnable, con(%p), enable(%i)", con, enable); - rsProgramVertexSetProjectionMatrixEnable(enable); -} - static jint nProgramVertexCreate(JNIEnv *_env, jobject _this) { @@ -1000,10 +976,7 @@ static JNINativeMethod methods[] = { {"nProgramVertexBindAllocation", "(III)V", (void*)nProgramVertexBindAllocation }, {"nProgramVertexBegin", "(II)V", (void*)nProgramVertexBegin }, {"nProgramVertexSetType", "(II)V", (void*)nProgramVertexSetType }, -{"nProgramVertexSetCameraMode", "(Z)V", (void*)nProgramVertexSetCameraMode }, {"nProgramVertexSetTextureMatrixEnable", "(Z)V", (void*)nProgramVertexSetTextureMatrixEnable }, -{"nProgramVertexSetModelMatrixEnable", "(Z)V", (void*)nProgramVertexSetModelMatrixEnable }, -{"nProgramVertexSetProjectionMatrixEnable", "(Z)V", (void*)nProgramVertexSetProjectionMatrixEnable }, {"nProgramVertexCreate", "()I", (void*)nProgramVertexCreate }, {"nContextBindRootScript", "(I)V", (void*)nContextBindRootScript }, diff --git a/libs/rs/rs.spec b/libs/rs/rs.spec index 107096f..dd489b8 100644 --- a/libs/rs/rs.spec +++ b/libs/rs/rs.spec @@ -286,10 +286,6 @@ ScriptCSetRoot { param bool isRoot } -ScriptCSetOrtho { - param bool isOrtho - } - ScriptCSetScript { param void * codePtr } @@ -403,19 +399,7 @@ ProgramVertexSetType { param RsType constants } -ProgramVertexSetCameraMode { - param bool ortho - } - ProgramVertexSetTextureMatrixEnable { param bool enable } -ProgramVertexSetModelMatrixEnable { - param bool enable - } - -ProgramVertexSetProjectionMatrixEnable { - param bool enable - } - diff --git a/libs/rs/rsContext.cpp b/libs/rs/rsContext.cpp index 266c455..7c46c0e 100644 --- a/libs/rs/rsContext.cpp +++ b/libs/rs/rsContext.cpp @@ -23,6 +23,7 @@ using namespace android; using namespace android::renderscript; Context * Context::gCon = NULL; +pthread_key_t Context::gThreadTLSKey = 0; void Context::initEGL() { @@ -81,24 +82,6 @@ bool Context::runRootScript() glEnable(GL_LIGHT0); glViewport(0, 0, mWidth, mHeight); - if(mRootScript->mEnviroment.mIsOrtho) { - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - glOrthof(0, mWidth, mHeight, 0, 0, 1); - glMatrixMode(GL_MODELVIEW); - } else { - float aspectH = ((float)mWidth) / mHeight; - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - glFrustumf(-1, 1, -aspectH, aspectH, 1, 100); - glRotatef(-90, 0,0,1); - glTranslatef(0, 0, -3); - glMatrixMode(GL_MODELVIEW); - } - - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - glDepthMask(GL_TRUE); glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); @@ -137,17 +120,33 @@ void * Context::threadProc(void *vrsc) rsc->mServerReturns.init(128); rsc->initEGL(); + + ScriptTLSStruct *tlsStruct = new ScriptTLSStruct; + if (!tlsStruct) { + LOGE("Error allocating tls storage"); + return NULL; + } + tlsStruct->mContext = rsc; + tlsStruct->mScript = NULL; + int status = pthread_setspecific(rsc->gThreadTLSKey, tlsStruct); + if (status) { + LOGE("pthread_setspecific %i", status); + } + + rsc->mStateVertex.init(rsc, rsc->mWidth, rsc->mHeight); + rsc->setVertex(NULL); + rsc->mStateFragment.init(rsc, rsc->mWidth, rsc->mHeight); + rsc->setFragment(NULL); + rsc->mStateFragmentStore.init(rsc, rsc->mWidth, rsc->mHeight); + rsc->setFragmentStore(NULL); + rsc->mRunning = true; bool mDraw = true; while (!rsc->mExit) { - mDraw |= gIO->playCoreCommands(rsc); - - if (!mDraw || !rsc->mRootScript.get()) { - usleep(10000); - continue; - } + mDraw |= gIO->playCoreCommands(rsc, !mDraw); + mDraw &= (rsc->mRootScript.get() != NULL); - if (rsc->mRootScript.get()) { + if (mDraw) { mDraw = rsc->runRootScript(); eglSwapBuffers(rsc->mDisplay, rsc->mSurface); } @@ -176,6 +175,12 @@ Context::Context(Device *dev, Surface *sur) int status; pthread_attr_t threadAttr; + status = pthread_key_create(&gThreadTLSKey, NULL); + if (status) { + LOGE("Failed to init thread tls key."); + return; + } + status = pthread_attr_init(&threadAttr); if (status) { LOGE("Failed to init thread attribute."); @@ -209,6 +214,7 @@ Context::~Context() if (mDev) { mDev->removeContext(this); + pthread_key_delete(gThreadTLSKey); } } @@ -230,20 +236,32 @@ void Context::setRootScript(Script *s) void Context::setFragmentStore(ProgramFragmentStore *pfs) { - mFragmentStore.set(pfs); - pfs->setupGL(); + if (pfs == NULL) { + mFragmentStore.set(mStateFragmentStore.mDefault); + } else { + mFragmentStore.set(pfs); + } + mFragmentStore->setupGL(); } void Context::setFragment(ProgramFragment *pf) { - mFragment.set(pf); - pf->setupGL(); + if (pf == NULL) { + mFragment.set(mStateFragment.mDefault); + } else { + mFragment.set(pf); + } + mFragment->setupGL(); } void Context::setVertex(ProgramVertex *pv) { - mVertex.set(pv); - pv->setupGL(); + if (pv == NULL) { + mVertex.set(mStateVertex.mDefault); + } else { + mVertex.set(pv); + } + mVertex->setupGL(); } void Context::assignName(ObjectBase *obj, const char *name, uint32_t len) diff --git a/libs/rs/rsContext.h b/libs/rs/rsContext.h index 21ae8c5..334ef3c 100644 --- a/libs/rs/rsContext.h +++ b/libs/rs/rsContext.h @@ -48,6 +48,12 @@ public: Context(Device *, Surface *); ~Context(); + static pthread_key_t gThreadTLSKey; + struct ScriptTLSStruct { + Context * mContext; + Script * mScript; + }; + //StructuredAllocationContext mStateAllocation; ElementState mStateElement; @@ -81,6 +87,17 @@ public: ObjectBase * lookupName(const char *name) const; void appendNameDefines(String8 *str) const; + + ProgramFragment * getDefaultProgramFragment() const { + return mStateFragment.mDefault.get(); + } + ProgramVertex * getDefaultProgramVertex() const { + return mStateVertex.mDefault.get(); + } + ProgramFragmentStore * getDefaultProgramFragmentStore() const { + return mStateFragmentStore.mDefault.get(); + } + protected: Device *mDev; @@ -107,10 +124,6 @@ protected: ObjectBaseRef<ProgramVertex> mVertex; ObjectBaseRef<ProgramFragmentStore> mFragmentStore; - ProgramFragment * mDefaultFragment; - ProgramVertex * mDefaultVertex; - ProgramFragmentStore * mDefaultFragmentStore; - private: Context(); diff --git a/libs/rs/rsLocklessFifo.cpp b/libs/rs/rsLocklessFifo.cpp index 67ab434..37ec14b 100644 --- a/libs/rs/rsLocklessFifo.cpp +++ b/libs/rs/rsLocklessFifo.cpp @@ -37,16 +37,8 @@ bool LocklessCommandFifo::init(uint32_t sizeInBytes) return false; } - int status = pthread_mutex_init(&mMutex, NULL); - if (status) { - LOGE("LocklessFifo mutex init failure"); - free(mBuffer); - return false; - } - status = pthread_cond_init(&mCondition, NULL); - if (status) { - LOGE("LocklessFifo condition init failure"); - pthread_mutex_destroy(&mMutex); + if (!mSignalToControl.init() || !mSignalToWorker.init()) { + LOGE("Signal setup failed"); free(mBuffer); return false; } @@ -106,6 +98,7 @@ void LocklessCommandFifo::commit(uint32_t command, uint32_t sizeInBytes) mPut += ((sizeInBytes + 3) & ~3) + 4; //dumpState("commit 2"); + mSignalToWorker.set(); } void LocklessCommandFifo::commitSync(uint32_t command, uint32_t sizeInBytes) @@ -118,7 +111,7 @@ void LocklessCommandFifo::flush() { //dumpState("flush 1"); while(mPut != mGet) { - usleep(1); + mSignalToControl.wait(); } //dumpState("flush 2"); } @@ -126,8 +119,10 @@ void LocklessCommandFifo::flush() const void * LocklessCommandFifo::get(uint32_t *command, uint32_t *bytesData) { while(1) { + //dumpState("get"); while(isEmpty()) { - usleep(10); + mSignalToControl.set(); + mSignalToWorker.wait(); } //dumpState("get 3"); @@ -149,6 +144,9 @@ void LocklessCommandFifo::next() { uint32_t bytes = reinterpret_cast<const uint16_t *>(mGet)[1]; mGet += ((bytes + 3) & ~3) + 4; + if (isEmpty()) { + mSignalToControl.set(); + } //dumpState("next"); } @@ -179,4 +177,79 @@ void LocklessCommandFifo::dumpState(const char *s) const LOGE("%s put %p, get %p, buf %p, end %p", s, mPut, mGet, mBuffer, mEnd); } +LocklessCommandFifo::Signal::Signal() +{ + mSet = true; +} + +LocklessCommandFifo::Signal::~Signal() +{ + pthread_mutex_destroy(&mMutex); + pthread_cond_destroy(&mCondition); +} + +bool LocklessCommandFifo::Signal::init() +{ + int status = pthread_mutex_init(&mMutex, NULL); + if (status) { + LOGE("LocklessFifo mutex init failure"); + return false; + } + + status = pthread_cond_init(&mCondition, NULL); + if (status) { + LOGE("LocklessFifo condition init failure"); + pthread_mutex_destroy(&mMutex); + return false; + } + + return true; +} + +void LocklessCommandFifo::Signal::set() +{ + int status; + + status = pthread_mutex_lock(&mMutex); + if (status) { + LOGE("LocklessCommandFifo: error %i locking for set condition.", status); + return; + } + + mSet = true; + + status = pthread_cond_signal(&mCondition); + if (status) { + LOGE("LocklessCommandFifo: error %i on set condition.", status); + } + + status = pthread_mutex_unlock(&mMutex); + if (status) { + LOGE("LocklessCommandFifo: error %i unlocking for set condition.", status); + } +} + +void LocklessCommandFifo::Signal::wait() +{ + int status; + + status = pthread_mutex_lock(&mMutex); + if (status) { + LOGE("LocklessCommandFifo: error %i locking for condition.", status); + return; + } + + if (!mSet) { + status = pthread_cond_wait(&mCondition, &mMutex); + if (status) { + LOGE("LocklessCommandFifo: error %i waiting on condition.", status); + } + } + mSet = false; + + status = pthread_mutex_unlock(&mMutex); + if (status) { + LOGE("LocklessCommandFifo: error %i unlocking for condition.", status); + } +} diff --git a/libs/rs/rsLocklessFifo.h b/libs/rs/rsLocklessFifo.h index ddef382..2f4d5c5 100644 --- a/libs/rs/rsLocklessFifo.h +++ b/libs/rs/rsLocklessFifo.h @@ -41,14 +41,32 @@ public: protected: + class Signal { + public: + Signal(); + ~Signal(); + + bool init(); + + void set(); + void wait(); + + protected: + bool mSet; + pthread_mutex_t mMutex; + pthread_cond_t mCondition; + }; + uint8_t * volatile mPut; uint8_t * volatile mGet; uint8_t * mBuffer; uint8_t * mEnd; uint8_t mSize; - pthread_mutex_t mMutex; - pthread_cond_t mCondition; + Signal mSignalToWorker; + Signal mSignalToControl; + + public: void * reserve(uint32_t bytes); diff --git a/libs/rs/rsMatrix.cpp b/libs/rs/rsMatrix.cpp index e68d5ac..258b836 100644 --- a/libs/rs/rsMatrix.cpp +++ b/libs/rs/rsMatrix.cpp @@ -136,4 +136,26 @@ void Matrix::loadMultiply(const Matrix *lhs, const Matrix *rhs) } } +void Matrix::loadOrtho(float l, float r, float b, float t, float n, float f) { + loadIdentity(); + m[0] = 2 / (r - l); + m[5] = 2 / (t - b); + m[10]= -2 / (f - n); + m[12]= -(r + l) / (r - l); + m[13]= -(t + b) / (t - b); + m[14]= -(f + n) / (f - n); +} + +void Matrix::loadFrustum(float l, float r, float b, float t, float n, float f) { + loadIdentity(); + m[0] = 2 * n / (r - l); + m[5] = 2 * n / (t - b); + m[8] = (r + l) / (r - l); + m[9] = (t + b) / (t - b); + m[10]= -(f + n) / (f - n); + m[11]= -1; + m[14]= -2*f*n / (f - n); + m[15]= 0; +} + diff --git a/libs/rs/rsMatrix.h b/libs/rs/rsMatrix.h index 619b494..7dc4165 100644 --- a/libs/rs/rsMatrix.h +++ b/libs/rs/rsMatrix.h @@ -44,6 +44,9 @@ struct Matrix void loadTranslate(float x, float y, float z); void loadMultiply(const Matrix *lhs, const Matrix *rhs); + void loadOrtho(float l, float r, float b, float t, float n, float f); + void loadFrustum(float l, float r, float b, float t, float n, float f); + void multiply(const Matrix *rhs) { Matrix tmp; tmp.loadMultiply(this, rhs); diff --git a/libs/rs/rsProgramFragment.cpp b/libs/rs/rsProgramFragment.cpp index 316e791..8777335 100644 --- a/libs/rs/rsProgramFragment.cpp +++ b/libs/rs/rsProgramFragment.cpp @@ -148,6 +148,11 @@ ProgramFragmentState::~ProgramFragmentState() } +void ProgramFragmentState::init(Context *rsc, int32_t w, int32_t h) +{ + ProgramFragment *pf = new ProgramFragment(NULL, NULL); + mDefault.set(pf); +} namespace android { diff --git a/libs/rs/rsProgramFragment.h b/libs/rs/rsProgramFragment.h index ed9c49b..896d8dd 100644 --- a/libs/rs/rsProgramFragment.h +++ b/libs/rs/rsProgramFragment.h @@ -82,9 +82,10 @@ public: ~ProgramFragmentState(); ProgramFragment *mPF; + void init(Context *rsc, int32_t w, int32_t h); ObjectBaseRef<Type> mTextureTypes[ProgramFragment::MAX_TEXTURE]; - + ObjectBaseRef<ProgramFragment> mDefault; Vector<ProgramFragment *> mPrograms; }; diff --git a/libs/rs/rsProgramFragmentStore.cpp b/libs/rs/rsProgramFragmentStore.cpp index a1855a6..96d37ae 100644 --- a/libs/rs/rsProgramFragmentStore.cpp +++ b/libs/rs/rsProgramFragmentStore.cpp @@ -201,6 +201,12 @@ ProgramFragmentStoreState::~ProgramFragmentStoreState() } +void ProgramFragmentStoreState::init(Context *rsc, int32_t w, int32_t h) +{ + ProgramFragmentStore *pfs = new ProgramFragmentStore(NULL, NULL); + mDefault.set(pfs); +} + namespace android { namespace renderscript { diff --git a/libs/rs/rsProgramFragmentStore.h b/libs/rs/rsProgramFragmentStore.h index d862775..bd3a9f4 100644 --- a/libs/rs/rsProgramFragmentStore.h +++ b/libs/rs/rsProgramFragmentStore.h @@ -74,7 +74,9 @@ class ProgramFragmentStoreState public: ProgramFragmentStoreState(); ~ProgramFragmentStoreState(); + void init(Context *rsc, int32_t w, int32_t h); + ObjectBaseRef<ProgramFragmentStore> mDefault; ProgramFragmentStore *mPFS; }; diff --git a/libs/rs/rsProgramVertex.cpp b/libs/rs/rsProgramVertex.cpp index fc26ab5..19afad5 100644 --- a/libs/rs/rsProgramVertex.cpp +++ b/libs/rs/rsProgramVertex.cpp @@ -25,14 +25,21 @@ ProgramVertex::ProgramVertex(Element *in, Element *out) : Program(in, out) { mTextureMatrixEnable = false; - mProjectionEnable = false; - mTransformEnable = false; } ProgramVertex::~ProgramVertex() { } +static void logMatrix(const char *txt, const float *f) +{ + LOGE("Matrix %s, %p", txt, f); + LOGE("%6.2f, %6.2f, %6.2f, %6.2f", f[0], f[4], f[8], f[12]); + LOGE("%6.2f, %6.2f, %6.2f, %6.2f", f[1], f[5], f[9], f[13]); + LOGE("%6.2f, %6.2f, %6.2f, %6.2f", f[2], f[6], f[10], f[14]); + LOGE("%6.2f, %6.2f, %6.2f, %6.2f", f[3], f[7], f[11], f[15]); +} + void ProgramVertex::setupGL() { const float *f = static_cast<const float *>(mConstants[0]->getPtr()); @@ -44,19 +51,13 @@ void ProgramVertex::setupGL() glLoadIdentity(); } + //logMatrix("prog", &f[RS_PROGRAM_VERTEX_PROJECTION_OFFSET]); + //logMatrix("model", &f[RS_PROGRAM_VERTEX_MODELVIEW_OFFSET]); glMatrixMode(GL_PROJECTION); - if (mProjectionEnable) { - glLoadMatrixf(&f[RS_PROGRAM_VERTEX_PROJECTION_OFFSET]); - } else { - } - + glLoadMatrixf(&f[RS_PROGRAM_VERTEX_PROJECTION_OFFSET]); glMatrixMode(GL_MODELVIEW); - if (mTransformEnable) { - glLoadMatrixf(&f[RS_PROGRAM_VERTEX_MODELVIEW_OFFSET]); - } else { - glLoadIdentity(); - } + glLoadMatrixf(&f[RS_PROGRAM_VERTEX_MODELVIEW_OFFSET]); } void ProgramVertex::setConstantType(uint32_t slot, const Type *t) @@ -80,6 +81,23 @@ ProgramVertexState::~ProgramVertexState() delete mPV; } +void ProgramVertexState::init(Context *rsc, int32_t w, int32_t h) +{ + ProgramVertex *pv = new ProgramVertex(NULL, NULL); + Allocation *alloc = (Allocation *) + rsi_AllocationCreatePredefSized(rsc, RS_ELEMENT_USER_FLOAT, 48); + mDefaultAlloc.set(alloc); + mDefault.set(pv); + + pv->bindAllocation(0, alloc); + + Matrix m; + m.loadOrtho(0,w, h,0, -1,1); + alloc->subData(RS_PROGRAM_VERTEX_PROJECTION_OFFSET, 16, &m.m[0]); + + m.loadIdentity(); + alloc->subData(RS_PROGRAM_VERTEX_MODELVIEW_OFFSET, 16, &m.m[0]); +} namespace android { @@ -110,26 +128,11 @@ void rsi_ProgramVertexSetType(Context *rsc, uint32_t slot, RsType constants) rsc->mStateVertex.mPV->setConstantType(slot, static_cast<const Type *>(constants)); } -void rsi_ProgramVertexSetCameraMode(Context *rsc, bool ortho) -{ - rsc->mStateVertex.mPV->setProjectionEnabled(!ortho); -} - void rsi_ProgramVertexSetTextureMatrixEnable(Context *rsc, bool enable) { rsc->mStateVertex.mPV->setTextureMatrixEnable(enable); } -void rsi_ProgramVertexSetModelMatrixEnable(Context *rsc, bool enable) -{ - rsc->mStateVertex.mPV->setTransformEnable(enable); -} - -void rsi_ProgramVertexSetProjectionMatrixEnable(Context *rsc, bool enable) -{ - rsc->mStateVertex.mPV->setProjectionEnable(enable); -} - } diff --git a/libs/rs/rsProgramVertex.h b/libs/rs/rsProgramVertex.h index 677be6e..1a92f01 100644 --- a/libs/rs/rsProgramVertex.h +++ b/libs/rs/rsProgramVertex.h @@ -38,9 +38,6 @@ public: void setConstantType(uint32_t slot, const Type *); void bindAllocation(uint32_t slot, Allocation *); void setTextureMatrixEnable(bool e) {mTextureMatrixEnable = e;} - void setProjectionEnabled(bool e) {mProjectionEnable = e;} - void setTransformEnable(bool e) {mTransformEnable = e;} - void setProjectionEnable(bool e) {mProjectionEnable = e;} protected: bool mDirty; @@ -50,8 +47,6 @@ protected: // Hacks to create a program for now bool mTextureMatrixEnable; - bool mProjectionEnable; - bool mTransformEnable; }; @@ -62,6 +57,11 @@ public: ProgramVertexState(); ~ProgramVertexState(); + void init(Context *rsc, int32_t w, int32_t h); + + ObjectBaseRef<ProgramVertex> mDefault; + ObjectBaseRef<Allocation> mDefaultAlloc; + ProgramVertex *mPV; //ObjectBaseRef<Type> mTextureTypes[ProgramFragment::MAX_TEXTURE]; diff --git a/libs/rs/rsScript.h b/libs/rs/rsScript.h index d32f116..7dd2b61 100644 --- a/libs/rs/rsScript.h +++ b/libs/rs/rsScript.h @@ -39,7 +39,6 @@ public: struct Enviroment_t { bool mIsRoot; - bool mIsOrtho; float mClearColor[4]; float mClearDepth; uint32_t mClearStencil; diff --git a/libs/rs/rsScriptC.cpp b/libs/rs/rsScriptC.cpp index d29eb9f..557f3ae 100644 --- a/libs/rs/rsScriptC.cpp +++ b/libs/rs/rsScriptC.cpp @@ -24,6 +24,11 @@ using namespace android; using namespace android::renderscript; +#define GET_TLS() Context::ScriptTLSStruct * tls = \ + (Context::ScriptTLSStruct *)pthread_getspecific(Context::gThreadTLSKey); \ + Context * rsc = tls->mContext; \ + ScriptC * sc = (ScriptC *) tls->mScript + ScriptC::ScriptC() { @@ -38,220 +43,212 @@ ScriptC::~ScriptC() } } -extern "C" void matrixLoadIdentity(void *con, rsc_Matrix *mat) +extern "C" void matrixLoadIdentity(rsc_Matrix *mat) { Matrix *m = reinterpret_cast<Matrix *>(mat); m->loadIdentity(); } -extern "C" void matrixLoadFloat(void *con, rsc_Matrix *mat, const float *f) +extern "C" void matrixLoadFloat(rsc_Matrix *mat, const float *f) { Matrix *m = reinterpret_cast<Matrix *>(mat); m->load(f); } -extern "C" void matrixLoadMat(void *con, rsc_Matrix *mat, const rsc_Matrix *newmat) +extern "C" void matrixLoadMat(rsc_Matrix *mat, const rsc_Matrix *newmat) { Matrix *m = reinterpret_cast<Matrix *>(mat); m->load(reinterpret_cast<const Matrix *>(newmat)); } -extern "C" void matrixLoadRotate(void *con, rsc_Matrix *mat, float rot, float x, float y, float z) +extern "C" void matrixLoadRotate(rsc_Matrix *mat, float rot, float x, float y, float z) { Matrix *m = reinterpret_cast<Matrix *>(mat); m->loadRotate(rot, x, y, z); } -extern "C" void matrixLoadScale(void *con, rsc_Matrix *mat, float x, float y, float z) +extern "C" void matrixLoadScale(rsc_Matrix *mat, float x, float y, float z) { Matrix *m = reinterpret_cast<Matrix *>(mat); m->loadScale(x, y, z); } -extern "C" void matrixLoadTranslate(void *con, rsc_Matrix *mat, float x, float y, float z) +extern "C" void matrixLoadTranslate(rsc_Matrix *mat, float x, float y, float z) { Matrix *m = reinterpret_cast<Matrix *>(mat); m->loadTranslate(x, y, z); } -extern "C" void matrixLoadMultiply(void *con, rsc_Matrix *mat, const rsc_Matrix *lhs, const rsc_Matrix *rhs) +extern "C" void matrixLoadMultiply(rsc_Matrix *mat, const rsc_Matrix *lhs, const rsc_Matrix *rhs) { Matrix *m = reinterpret_cast<Matrix *>(mat); m->loadMultiply(reinterpret_cast<const Matrix *>(lhs), reinterpret_cast<const Matrix *>(rhs)); } -extern "C" void matrixMultiply(void *con, rsc_Matrix *mat, const rsc_Matrix *rhs) +extern "C" void matrixMultiply(rsc_Matrix *mat, const rsc_Matrix *rhs) { Matrix *m = reinterpret_cast<Matrix *>(mat); m->multiply(reinterpret_cast<const Matrix *>(rhs)); } -extern "C" void matrixRotate(void *con, rsc_Matrix *mat, float rot, float x, float y, float z) +extern "C" void matrixRotate(rsc_Matrix *mat, float rot, float x, float y, float z) { Matrix *m = reinterpret_cast<Matrix *>(mat); m->rotate(rot, x, y, z); } -extern "C" void matrixScale(void *con, rsc_Matrix *mat, float x, float y, float z) +extern "C" void matrixScale(rsc_Matrix *mat, float x, float y, float z) { Matrix *m = reinterpret_cast<Matrix *>(mat); m->scale(x, y, z); } -extern "C" void matrixTranslate(void *con, rsc_Matrix *mat, float x, float y, float z) +extern "C" void matrixTranslate(rsc_Matrix *mat, float x, float y, float z) { Matrix *m = reinterpret_cast<Matrix *>(mat); m->translate(x, y, z); } -extern "C" const void * loadVp(void *vp, uint32_t bank, uint32_t offset) +extern "C" const void * loadVp(uint32_t bank, uint32_t offset) { - ScriptC::Env * env = static_cast<ScriptC::Env *>(vp); - return &static_cast<const uint8_t *>(env->mScript->mSlots[bank]->getPtr())[offset]; + GET_TLS(); + return &static_cast<const uint8_t *>(sc->mSlots[bank]->getPtr())[offset]; } -extern "C" float loadF(void *vp, uint32_t bank, uint32_t offset) +extern "C" float loadF(uint32_t bank, uint32_t offset) { - ScriptC::Env * env = static_cast<ScriptC::Env *>(vp); - //LOGE("bank %i, offset %i", bank, offset); - //LOGE("%p", env->mScript->mSlots[bank]->getPtr()); - return static_cast<const float *>(env->mScript->mSlots[bank]->getPtr())[offset]; + GET_TLS(); + return static_cast<const float *>(sc->mSlots[bank]->getPtr())[offset]; } -extern "C" int32_t loadI32(void *vp, uint32_t bank, uint32_t offset) +extern "C" int32_t loadI32(uint32_t bank, uint32_t offset) { - ScriptC::Env * env = static_cast<ScriptC::Env *>(vp); - return static_cast<const int32_t *>(env->mScript->mSlots[bank]->getPtr())[offset]; + GET_TLS(); + return static_cast<const int32_t *>(sc->mSlots[bank]->getPtr())[offset]; } -extern "C" uint32_t loadU32(void *vp, uint32_t bank, uint32_t offset) +extern "C" uint32_t loadU32(uint32_t bank, uint32_t offset) { - ScriptC::Env * env = static_cast<ScriptC::Env *>(vp); - return static_cast<const uint32_t *>(env->mScript->mSlots[bank]->getPtr())[offset]; + GET_TLS(); + return static_cast<const uint32_t *>(sc->mSlots[bank]->getPtr())[offset]; } -extern "C" void loadEnvVec4(void *vp, uint32_t bank, uint32_t offset, rsc_Vector4 *v) +extern "C" void loadEnvVec4(uint32_t bank, uint32_t offset, rsc_Vector4 *v) { - ScriptC::Env * env = static_cast<ScriptC::Env *>(vp); - memcpy(v, &static_cast<const float *>(env->mScript->mSlots[bank]->getPtr())[offset], sizeof(rsc_Vector4)); + GET_TLS(); + memcpy(v, &static_cast<const float *>(sc->mSlots[bank]->getPtr())[offset], sizeof(rsc_Vector4)); } -extern "C" void loadEnvMatrix(void *vp, uint32_t bank, uint32_t offset, rsc_Matrix *m) +extern "C" void loadEnvMatrix(uint32_t bank, uint32_t offset, rsc_Matrix *m) { - ScriptC::Env * env = static_cast<ScriptC::Env *>(vp); - memcpy(m, &static_cast<const float *>(env->mScript->mSlots[bank]->getPtr())[offset], sizeof(rsc_Matrix)); + GET_TLS(); + memcpy(m, &static_cast<const float *>(sc->mSlots[bank]->getPtr())[offset], sizeof(rsc_Matrix)); } -extern "C" void storeF(void *vp, uint32_t bank, uint32_t offset, float v) +extern "C" void storeF(uint32_t bank, uint32_t offset, float v) { - ScriptC::Env * env = static_cast<ScriptC::Env *>(vp); - static_cast<float *>(env->mScript->mSlots[bank]->getPtr())[offset] = v; + GET_TLS(); + static_cast<float *>(sc->mSlots[bank]->getPtr())[offset] = v; } -extern "C" void storeI32(void *vp, uint32_t bank, uint32_t offset, int32_t v) +extern "C" void storeI32(uint32_t bank, uint32_t offset, int32_t v) { - ScriptC::Env * env = static_cast<ScriptC::Env *>(vp); - static_cast<int32_t *>(env->mScript->mSlots[bank]->getPtr())[offset] = v; + GET_TLS(); + static_cast<int32_t *>(sc->mSlots[bank]->getPtr())[offset] = v; } -extern "C" void storeU32(void *vp, uint32_t bank, uint32_t offset, uint32_t v) +extern "C" void storeU32(uint32_t bank, uint32_t offset, uint32_t v) { - ScriptC::Env * env = static_cast<ScriptC::Env *>(vp); - static_cast<uint32_t *>(env->mScript->mSlots[bank]->getPtr())[offset] = v; + GET_TLS(); + static_cast<uint32_t *>(sc->mSlots[bank]->getPtr())[offset] = v; } -extern "C" void storeEnvVec4(void *vp, uint32_t bank, uint32_t offset, const rsc_Vector4 *v) +extern "C" void storeEnvVec4(uint32_t bank, uint32_t offset, const rsc_Vector4 *v) { - ScriptC::Env * env = static_cast<ScriptC::Env *>(vp); - memcpy(&static_cast<float *>(env->mScript->mSlots[bank]->getPtr())[offset], v, sizeof(rsc_Vector4)); + GET_TLS(); + memcpy(&static_cast<float *>(sc->mSlots[bank]->getPtr())[offset], v, sizeof(rsc_Vector4)); } -extern "C" void storeEnvMatrix(void *vp, uint32_t bank, uint32_t offset, const rsc_Matrix *m) +extern "C" void storeEnvMatrix(uint32_t bank, uint32_t offset, const rsc_Matrix *m) { - ScriptC::Env * env = static_cast<ScriptC::Env *>(vp); - memcpy(&static_cast<float *>(env->mScript->mSlots[bank]->getPtr())[offset], m, sizeof(rsc_Matrix)); + GET_TLS(); + memcpy(&static_cast<float *>(sc->mSlots[bank]->getPtr())[offset], m, sizeof(rsc_Matrix)); } -extern "C" void color(void *vp, float r, float g, float b, float a) +extern "C" void color(float r, float g, float b, float a) { - ScriptC::Env * env = static_cast<ScriptC::Env *>(vp); glColor4f(r, g, b, a); } -extern "C" void renderTriangleMesh(void *vp, RsTriangleMesh mesh) +extern "C" void renderTriangleMesh(RsTriangleMesh mesh) { - ScriptC::Env * env = static_cast<ScriptC::Env *>(vp); - rsi_TriangleMeshRender(env->mContext, mesh); + GET_TLS(); + rsi_TriangleMeshRender(rsc, mesh); } -extern "C" void renderTriangleMeshRange(void *vp, RsTriangleMesh mesh, uint32_t start, uint32_t count) +extern "C" void renderTriangleMeshRange(RsTriangleMesh mesh, uint32_t start, uint32_t count) { - ScriptC::Env * env = static_cast<ScriptC::Env *>(vp); - rsi_TriangleMeshRenderRange(env->mContext, mesh, start, count); + GET_TLS(); + rsi_TriangleMeshRenderRange(rsc, mesh, start, count); } -extern "C" void materialDiffuse(void *vp, float r, float g, float b, float a) +extern "C" void materialDiffuse(float r, float g, float b, float a) { - ScriptC::Env * env = static_cast<ScriptC::Env *>(vp); float v[] = {r, g, b, a}; glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, v); } -extern "C" void materialSpecular(void *vp, float r, float g, float b, float a) +extern "C" void materialSpecular(float r, float g, float b, float a) { - ScriptC::Env * env = static_cast<ScriptC::Env *>(vp); float v[] = {r, g, b, a}; glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, v); } -extern "C" void lightPosition(void *vp, float x, float y, float z, float w) +extern "C" void lightPosition(float x, float y, float z, float w) { - ScriptC::Env * env = static_cast<ScriptC::Env *>(vp); float v[] = {x, y, z, w}; glLightfv(GL_LIGHT0, GL_POSITION, v); } -extern "C" void materialShininess(void *vp, float s) +extern "C" void materialShininess(float s) { - ScriptC::Env * env = static_cast<ScriptC::Env *>(vp); glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, &s); } -extern "C" void uploadToTexture(void *vp, RsAllocation va, uint32_t baseMipLevel) +extern "C" void uploadToTexture(RsAllocation va, uint32_t baseMipLevel) { - ScriptC::Env * env = static_cast<ScriptC::Env *>(vp); - rsi_AllocationUploadToTexture(env->mContext, va, baseMipLevel); + GET_TLS(); + rsi_AllocationUploadToTexture(rsc, va, baseMipLevel); } -extern "C" void enable(void *vp, uint32_t p) +extern "C" void enable(uint32_t p) { - ScriptC::Env * env = static_cast<ScriptC::Env *>(vp); glEnable(p); } -extern "C" void disable(void *vp, uint32_t p) +extern "C" void disable(uint32_t p) { - ScriptC::Env * env = static_cast<ScriptC::Env *>(vp); glDisable(p); } -extern "C" uint32_t scriptRand(void *vp, uint32_t max) +extern "C" uint32_t scriptRand(uint32_t max) { return (uint32_t)(((float)rand()) * max / RAND_MAX); } // Assumes (GL_FIXED) x,y,z (GL_UNSIGNED_BYTE)r,g,b,a -extern "C" void drawTriangleArray(void *vp, RsAllocation alloc, uint32_t count) +extern "C" void drawTriangleArray(RsAllocation alloc, uint32_t count) { + GET_TLS(); + const Allocation *a = (const Allocation *)alloc; const uint32_t *ptr = (const uint32_t *)a->getPtr(); - ScriptC::Env * env = static_cast<ScriptC::Env *>(vp); - env->mContext->setupCheck(); + rsc->setupCheck(); glBindBuffer(GL_ARRAY_BUFFER, 0); //glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, tm->mBufferObjects[1]); @@ -268,8 +265,9 @@ extern "C" void drawTriangleArray(void *vp, RsAllocation alloc, uint32_t count) glDrawArrays(GL_TRIANGLES, 0, count * 3); } -extern "C" void drawRect(void *vp, int32_t x1, int32_t x2, int32_t y1, int32_t y2) +extern "C" void drawRect(int32_t x1, int32_t x2, int32_t y1, int32_t y2) { + GET_TLS(); x1 = (x1 << 16); x2 = (x2 << 16); y1 = (y1 << 16); @@ -279,8 +277,7 @@ extern "C" void drawRect(void *vp, int32_t x1, int32_t x2, int32_t y1, int32_t y static const int32_t tex[] = {0,0, 0,0x10000, 0x10000,0, 0x10000,0x10000}; - ScriptC::Env * env = static_cast<ScriptC::Env *>(vp); - env->mContext->setupCheck(); + rsc->setupCheck(); glBindBuffer(GL_ARRAY_BUFFER, 0); //glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, tm->mBufferObjects[1]); @@ -297,40 +294,37 @@ extern "C" void drawRect(void *vp, int32_t x1, int32_t x2, int32_t y1, int32_t y glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); } -extern "C" void pfBindTexture(void *vp, RsProgramFragment vpf, uint32_t slot, RsAllocation va) +extern "C" void pfBindTexture(RsProgramFragment vpf, uint32_t slot, RsAllocation va) { - //LOGE("pfBindTexture %p", vpf); - ScriptC::Env * env = static_cast<ScriptC::Env *>(vp); - rsi_ProgramFragmentBindTexture(env->mContext, + GET_TLS(); + rsi_ProgramFragmentBindTexture(rsc, static_cast<ProgramFragment *>(vpf), slot, static_cast<Allocation *>(va)); } -extern "C" void pfBindSampler(void *vp, RsProgramFragment vpf, uint32_t slot, RsSampler vs) +extern "C" void pfBindSampler(RsProgramFragment vpf, uint32_t slot, RsSampler vs) { - ScriptC::Env * env = static_cast<ScriptC::Env *>(vp); - rsi_ProgramFragmentBindSampler(env->mContext, + GET_TLS(); + rsi_ProgramFragmentBindSampler(rsc, static_cast<ProgramFragment *>(vpf), slot, static_cast<Sampler *>(vs)); } -extern "C" void contextBindProgramFragmentStore(void *vp, RsProgramFragmentStore pfs) +extern "C" void contextBindProgramFragmentStore(RsProgramFragmentStore pfs) { - //LOGE("contextBindProgramFragmentStore %p", pfs); - ScriptC::Env * env = static_cast<ScriptC::Env *>(vp); - rsi_ContextBindProgramFragmentStore(env->mContext, pfs); + GET_TLS(); + rsi_ContextBindProgramFragmentStore(rsc, pfs); } -extern "C" void contextBindProgramFragment(void *vp, RsProgramFragment pf) +extern "C" void contextBindProgramFragment(RsProgramFragment pf) { - //LOGE("contextBindProgramFragment %p", pf); - ScriptC::Env * env = static_cast<ScriptC::Env *>(vp); - rsi_ContextBindProgramFragment(env->mContext, pf); + GET_TLS(); + rsi_ContextBindProgramFragment(rsc, pf); } @@ -388,9 +382,10 @@ static rsc_FunctionTable scriptCPtrTable = { }; -bool ScriptC::run(Context *rsc, uint32_t launchID) +bool ScriptC::run(Context *rsc, uint32_t launchIndex) { - Env e = {rsc, this}; + Context::ScriptTLSStruct * tls = + (Context::ScriptTLSStruct *)pthread_getspecific(Context::gThreadTLSKey); if (mEnviroment.mFragmentStore.get()) { rsc->setFragmentStore(mEnviroment.mFragmentStore.get()); @@ -398,8 +393,13 @@ bool ScriptC::run(Context *rsc, uint32_t launchID) if (mEnviroment.mFragment.get()) { rsc->setFragment(mEnviroment.mFragment.get()); } + if (mEnviroment.mVertex.get()) { + rsc->setVertex(mEnviroment.mVertex.get()); + } - return mProgram.mScript(&e, &scriptCPtrTable, launchID) != 0; + tls->mScript = this; + return mProgram.mScript(launchIndex, &scriptCPtrTable) != 0; + tls->mScript = NULL; } ScriptCState::ScriptCState() @@ -428,7 +428,6 @@ void ScriptCState::clear() mEnviroment.mClearDepth = 1; mEnviroment.mClearStencil = 0; mEnviroment.mIsRoot = false; - mEnviroment.mIsOrtho = true; mAccScript = NULL; @@ -449,6 +448,9 @@ void ScriptCState::runCompiler(Context *rsc) accGetScriptLabel(mAccScript, "main", (ACCvoid**) &mProgram.mScript); rsAssert(mProgram.mScript); + mEnviroment.mFragment.set(rsc->getDefaultProgramFragment()); + mEnviroment.mVertex.set(rsc->getDefaultProgramVertex()); + mEnviroment.mFragmentStore.set(rsc->getDefaultProgramFragmentStore()); if (mProgram.mScript) { const static int pragmaMax = 16; @@ -466,6 +468,18 @@ void ScriptCState::runCompiler(Context *rsc) if (!strcmp(str[ct], "stateVertex")) { + if (!strcmp(str[ct+1], "default")) { + continue; + } + if (!strcmp(str[ct+1], "parent")) { + mEnviroment.mVertex.clear(); + continue; + } + ProgramVertex * pv = (ProgramVertex *)rsc->lookupName(str[ct+1]); + if (pv != NULL) { + mEnviroment.mVertex.set(pv); + continue; + } LOGE("Unreconized value %s passed to stateVertex", str[ct+1]); } @@ -474,8 +488,14 @@ void ScriptCState::runCompiler(Context *rsc) } if (!strcmp(str[ct], "stateFragment")) { - ProgramFragment * pf = - (ProgramFragment *)rsc->lookupName(str[ct+1]); + if (!strcmp(str[ct+1], "default")) { + continue; + } + if (!strcmp(str[ct+1], "parent")) { + mEnviroment.mFragment.clear(); + continue; + } + ProgramFragment * pf = (ProgramFragment *)rsc->lookupName(str[ct+1]); if (pf != NULL) { mEnviroment.mFragment.set(pf); continue; @@ -484,18 +504,19 @@ void ScriptCState::runCompiler(Context *rsc) } if (!strcmp(str[ct], "stateFragmentStore")) { + if (!strcmp(str[ct+1], "default")) { + continue; + } + if (!strcmp(str[ct+1], "parent")) { + mEnviroment.mFragmentStore.clear(); + continue; + } ProgramFragmentStore * pfs = (ProgramFragmentStore *)rsc->lookupName(str[ct+1]); if (pfs != NULL) { mEnviroment.mFragmentStore.set(pfs); continue; } - - if (!strcmp(str[ct+1], "parent")) { - //mEnviroment.mStateFragmentStore = - //Script::Enviroment_t::FRAGMENT_STORE_PARENT; - continue; - } LOGE("Unreconized value %s passed to stateFragmentStore", str[ct+1]); } @@ -556,12 +577,6 @@ void rsi_ScriptCSetRoot(Context * rsc, bool isRoot) ss->mEnviroment.mIsRoot = isRoot; } -void rsi_ScriptCSetOrtho(Context * rsc, bool isOrtho) -{ - ScriptCState *ss = &rsc->mScriptC; - ss->mEnviroment.mIsOrtho = isOrtho; -} - void rsi_ScriptCSetText(Context *rsc, const char *text, uint32_t len) { ScriptCState *ss = &rsc->mScriptC; diff --git a/libs/rs/rsScriptC.h b/libs/rs/rsScriptC.h index 55a2cc6..c46901b1 100644 --- a/libs/rs/rsScriptC.h +++ b/libs/rs/rsScriptC.h @@ -52,12 +52,6 @@ public: ACCscript* mAccScript; virtual bool run(Context *, uint32_t launchID); - - struct Env { - Context *mContext; - ScriptC *mScript; - }; - }; class ScriptCState diff --git a/libs/rs/rsThreadIO.cpp b/libs/rs/rsThreadIO.cpp index 23c808a..5641581 100644 --- a/libs/rs/rsThreadIO.cpp +++ b/libs/rs/rsThreadIO.cpp @@ -34,16 +34,17 @@ ThreadIO::~ThreadIO() { } -bool ThreadIO::playCoreCommands(Context *con) +bool ThreadIO::playCoreCommands(Context *con, bool waitForCommand) { //LOGE("playCoreCommands 1"); uint32_t cmdID = 0; uint32_t cmdSize = 0; bool ret = false; - while(!mToCore.isEmpty()) { + while(!mToCore.isEmpty() || waitForCommand) { ret = true; //LOGE("playCoreCommands 2"); const void * data = mToCore.get(&cmdID, &cmdSize); + waitForCommand = false; //LOGE("playCoreCommands 3 %i %i", cmdID, cmdSize); gPlaybackFuncs[cmdID](con, data); diff --git a/libs/rs/rsThreadIO.h b/libs/rs/rsThreadIO.h index ae2ffc0..973ee05 100644 --- a/libs/rs/rsThreadIO.h +++ b/libs/rs/rsThreadIO.h @@ -37,7 +37,7 @@ public: // Plays back commands from the client. // Returns true if any commands were processed. - bool playCoreCommands(Context *con); + bool playCoreCommands(Context *con, bool waitForCommand); LocklessCommandFifo mToCore; diff --git a/libs/surfaceflinger/DisplayHardware/DisplayHardware.cpp b/libs/surfaceflinger/DisplayHardware/DisplayHardware.cpp index 3f19c85..784dfa5 100644 --- a/libs/surfaceflinger/DisplayHardware/DisplayHardware.cpp +++ b/libs/surfaceflinger/DisplayHardware/DisplayHardware.cpp @@ -218,9 +218,14 @@ void DisplayHardware::init(uint32_t dpy) mRefreshRate = fbDev->fps; char property[PROPERTY_VALUE_MAX]; - if (property_get("ro.sf.lcd_density", property, NULL) <= 0) { - LOGW("ro.sf.lcd_density not defined, using 160 dpi by default."); - strcpy(property, "160"); + /* Read density from build-specific ro.sf.lcd_density property + * except if it is overriden by qemu.sf.lcd_density. + */ + if (property_get("qemu.sf.lcd_density", property, NULL) <= 0) { + if (property_get("ro.sf.lcd_density", property, NULL) <= 0) { + LOGW("ro.sf.lcd_density not defined, using 160 dpi by default."); + strcpy(property, "160"); + } } mDensity = atoi(property) * (1.0f/160.0f); diff --git a/libs/utils/BackupData.cpp b/libs/utils/BackupData.cpp index 8c9f875..6a7f056 100644 --- a/libs/utils/BackupData.cpp +++ b/libs/utils/BackupData.cpp @@ -86,46 +86,6 @@ BackupDataWriter::write_padding_for(int n) } status_t -BackupDataWriter::WriteAppHeader(const String8& packageName, int cookie) -{ - if (m_status != NO_ERROR) { - return m_status; - } - - ssize_t amt; - - amt = write_padding_for(m_pos); - if (amt != 0) { - return amt; - } - - app_header_v1 header; - ssize_t nameLen; - - nameLen = packageName.length(); - - header.type = tolel(BACKUP_HEADER_APP_V1); - header.packageLen = tolel(nameLen); - header.cookie = cookie; - - amt = write(m_fd, &header, sizeof(app_header_v1)); - if (amt != sizeof(app_header_v1)) { - m_status = errno; - return m_status; - } - m_pos += amt; - - amt = write(m_fd, packageName.string(), nameLen+1); - if (amt != nameLen+1) { - m_status = errno; - return m_status; - } - m_pos += amt; - - return NO_ERROR; -} - -status_t BackupDataWriter::WriteEntityHeader(const String8& key, size_t dataSize) { if (m_status != NO_ERROR) { @@ -188,40 +148,11 @@ BackupDataWriter::WriteEntityData(const void* data, size_t size) return NO_ERROR; } -status_t -BackupDataWriter::WriteAppFooter(int cookie) -{ - if (m_status != NO_ERROR) { - return m_status; - } - - ssize_t amt; - - amt = write_padding_for(m_pos); - if (amt != 0) { - return amt; - } - - app_footer_v1 footer; - ssize_t nameLen; - - footer.type = tolel(BACKUP_FOOTER_APP_V1); - footer.entityCount = tolel(m_entityCount); - footer.cookie = cookie; - - amt = write(m_fd, &footer, sizeof(app_footer_v1)); - if (amt != sizeof(app_footer_v1)) { - m_status = errno; - return m_status; - } - m_pos += amt; - - return NO_ERROR; -} BackupDataReader::BackupDataReader(int fd) :m_fd(fd), + m_done(false), m_status(NO_ERROR), m_pos(0), m_entityCount(0) @@ -260,32 +191,31 @@ BackupDataReader::Status() } while(0) status_t -BackupDataReader::ReadNextHeader(int* type) +BackupDataReader::ReadNextHeader(bool* done, int* type) { + *done = m_done; if (m_status != NO_ERROR) { return m_status; } int amt; - SKIP_PADDING(); + // No error checking here, in case we're at the end of the stream. Just let read() fail. + skip_padding(); amt = read(m_fd, &m_header, sizeof(m_header)); + *done = m_done = (amt == 0); CHECK_SIZE(amt, sizeof(m_header)); + m_pos += sizeof(m_header); + if (type) { + *type = m_header.type; + } // validate and fix up the fields. m_header.type = fromlel(m_header.type); switch (m_header.type) { - case BACKUP_HEADER_APP_V1: - m_header.app.packageLen = fromlel(m_header.app.packageLen); - if (m_header.app.packageLen < 0) { - LOGD("App header at %d has packageLen<0: 0x%08x\n", (int)m_pos, - (int)m_header.app.packageLen); - m_status = EINVAL; - } - m_header.app.cookie = m_header.app.cookie; - break; case BACKUP_HEADER_ENTITY_V1: + { m_header.entity.keyLen = fromlel(m_header.entity.keyLen); if (m_header.entity.keyLen <= 0) { LOGD("Entity header at %d has keyLen<=0: 0x%08x\n", (int)m_pos, @@ -294,52 +224,31 @@ BackupDataReader::ReadNextHeader(int* type) } m_header.entity.dataSize = fromlel(m_header.entity.dataSize); m_entityCount++; - break; - case BACKUP_FOOTER_APP_V1: - m_header.footer.entityCount = fromlel(m_header.footer.entityCount); - if (m_header.footer.entityCount < 0) { - LOGD("Entity header at %d has entityCount<0: 0x%08x\n", (int)m_pos, - (int)m_header.footer.entityCount); - m_status = EINVAL; + + // read the rest of the header (filename) + size_t size = m_header.entity.keyLen; + char* buf = m_key.lockBuffer(size); + if (buf == NULL) { + m_status = ENOMEM; + return m_status; } - m_header.footer.cookie = m_header.footer.cookie; + int amt = read(m_fd, buf, size+1); + CHECK_SIZE(amt, (int)size+1); + m_key.unlockBuffer(size); + m_pos += size+1; + SKIP_PADDING(); + m_dataEndPos = m_pos + m_header.entity.dataSize; + break; + } default: LOGD("Chunk header at %d has invalid type: 0x%08x", (int)m_pos, (int)m_header.type); m_status = EINVAL; } - m_pos += sizeof(m_header); - if (type) { - *type = m_header.type; - } return m_status; } -status_t -BackupDataReader::ReadAppHeader(String8* packageName, int* cookie) -{ - if (m_status != NO_ERROR) { - return m_status; - } - if (m_header.type != BACKUP_HEADER_APP_V1) { - return EINVAL; - } - size_t size = m_header.app.packageLen; - char* buf = packageName->lockBuffer(size); - if (buf == NULL) { - packageName->unlockBuffer(); - m_status = ENOMEM; - return m_status; - } - int amt = read(m_fd, buf, size+1); - CHECK_SIZE(amt, (int)size+1); - packageName->unlockBuffer(size); - m_pos += size+1; - *cookie = m_header.app.cookie; - return NO_ERROR; -} - bool BackupDataReader::HasEntities() { @@ -355,19 +264,8 @@ BackupDataReader::ReadEntityHeader(String8* key, size_t* dataSize) if (m_header.type != BACKUP_HEADER_ENTITY_V1) { return EINVAL; } - size_t size = m_header.entity.keyLen; - char* buf = key->lockBuffer(size); - if (key == NULL) { - key->unlockBuffer(); - m_status = ENOMEM; - return m_status; - } - int amt = read(m_fd, buf, size+1); - CHECK_SIZE(amt, (int)size+1); - key->unlockBuffer(size); - m_pos += size+1; + *key = m_key; *dataSize = m_header.entity.dataSize; - SKIP_PADDING(); return NO_ERROR; } @@ -381,42 +279,36 @@ BackupDataReader::SkipEntityData() return EINVAL; } if (m_header.entity.dataSize > 0) { - int pos = lseek(m_fd, m_header.entity.dataSize, SEEK_CUR); + int pos = lseek(m_fd, m_dataEndPos, SEEK_SET); return pos == -1 ? (int)errno : (int)NO_ERROR; } else { return NO_ERROR; } } -status_t +ssize_t BackupDataReader::ReadEntityData(void* data, size_t size) { if (m_status != NO_ERROR) { - return m_status; + return -1; } - int amt = read(m_fd, data, size); - CHECK_SIZE(amt, (int)size); - m_pos += size; - return NO_ERROR; -} - -status_t -BackupDataReader::ReadAppFooter(int* cookie) -{ - if (m_status != NO_ERROR) { - return m_status; + int remaining = m_dataEndPos - m_pos; + //LOGD("ReadEntityData size=%d m_pos=0x%x m_dataEndPos=0x%x remaining=%d\n", + // size, m_pos, m_dataEndPos, remaining); + if (remaining <= 0) { + return 0; } - if (m_header.type != BACKUP_FOOTER_APP_V1) { - return EINVAL; + if (size > remaining) { + size = remaining; } - if (m_header.footer.entityCount != m_entityCount) { - LOGD("entity count mismatch actual=%d expected=%d", m_entityCount, - m_header.footer.entityCount); - m_status = EINVAL; - return m_status; + //LOGD(" reading %d bytes", size); + int amt = read(m_fd, data, size); + if (amt < 0) { + m_status = errno; + return -1; } - *cookie = m_header.footer.cookie; - return NO_ERROR; + m_pos += amt; + return amt; } status_t diff --git a/libs/utils/BackupHelpers.cpp b/libs/utils/BackupHelpers.cpp index 4c3e37d..d65a457 100644 --- a/libs/utils/BackupHelpers.cpp +++ b/libs/utils/BackupHelpers.cpp @@ -47,27 +47,6 @@ namespace android { #define LOGP(x...) LOGD(x) #endif -struct SnapshotHeader { - int magic0; - int fileCount; - int magic1; - int totalSize; -}; - -struct FileState { - int modTime_sec; - int modTime_nsec; - int size; - int crc32; - int nameLen; -}; - -struct FileRec { - char const* file; // this object does not own this string - bool deleted; - FileState s; -}; - const static int ROUND_UP[4] = { 0, 3, 2, 1 }; static inline int @@ -310,7 +289,8 @@ back_up_files(int oldSnapshotFD, BackupDataWriter* dataStream, int newSnapshotFD for (int i=0; i<fileCount; i++) { String8 key(keys[i]); FileRec r; - char const* file = r.file = files[i]; + char const* file = files[i]; + r.file = file; struct stat st; err = stat(file, &st); @@ -351,20 +331,20 @@ back_up_files(int oldSnapshotFD, BackupDataWriter* dataStream, int newSnapshotFD } else if (cmp > 0) { // file added - LOGP("file added: %s", g.file); - write_update_file(dataStream, q, g.file); + LOGP("file added: %s", g.file.string()); + write_update_file(dataStream, q, g.file.string()); m++; } else { // both files exist, check them const FileState& f = oldSnapshot.valueAt(n); - int fd = open(g.file, O_RDONLY); + int fd = open(g.file.string(), O_RDONLY); if (fd < 0) { // We can't open the file. Don't report it as a delete either. Let the // server keep the old version. Maybe they'll be able to deal with it // on restore. - LOGP("Unable to open file %s - skipping", g.file); + LOGP("Unable to open file %s - skipping", g.file.string()); } else { g.s.crc32 = compute_crc32(fd); @@ -375,7 +355,7 @@ back_up_files(int oldSnapshotFD, BackupDataWriter* dataStream, int newSnapshotFD g.s.modTime_sec, g.s.modTime_nsec, g.s.size, g.s.crc32); if (f.modTime_sec != g.s.modTime_sec || f.modTime_nsec != g.s.modTime_nsec || f.size != g.s.size || f.crc32 != g.s.crc32) { - write_update_file(dataStream, fd, p, g.file); + write_update_file(dataStream, fd, p, g.file.string()); } close(fd); @@ -395,7 +375,7 @@ back_up_files(int oldSnapshotFD, BackupDataWriter* dataStream, int newSnapshotFD while (m<fileCount) { const String8& q = newSnapshot.keyAt(m); FileRec& g = newSnapshot.editValueAt(m); - write_update_file(dataStream, q, g.file); + write_update_file(dataStream, q, g.file.string()); m++; } @@ -404,6 +384,86 @@ back_up_files(int oldSnapshotFD, BackupDataWriter* dataStream, int newSnapshotFD return 0; } +#define RESTORE_BUF_SIZE (8*1024) + +RestoreHelperBase::RestoreHelperBase() +{ + m_buf = malloc(RESTORE_BUF_SIZE); +} + +RestoreHelperBase::~RestoreHelperBase() +{ + free(m_buf); +} + +status_t +RestoreHelperBase::WriteFile(const String8& filename, BackupDataReader* in) +{ + ssize_t err; + size_t dataSize; + String8 key; + int fd; + void* buf = m_buf; + ssize_t amt; + int mode; + int crc; + struct stat st; + FileRec r; + + err = in->ReadEntityHeader(&key, &dataSize); + if (err != NO_ERROR) { + return err; + } + + // TODO: World readable/writable for now. + mode = 0666; + + // Write the file and compute the crc + crc = crc32(0L, Z_NULL, 0); + fd = open(filename.string(), O_CREAT|O_RDWR|O_TRUNC, mode); + if (fd == -1) { + LOGW("Could not open file %s -- %s", filename.string(), strerror(errno)); + return errno; + } + + while ((amt = in->ReadEntityData(buf, RESTORE_BUF_SIZE)) > 0) { + err = write(fd, buf, amt); + if (err != amt) { + close(fd); + LOGW("Error '%s' writing '%s'", strerror(errno), filename.string()); + return errno; + } + crc = crc32(crc, (Bytef*)buf, amt); + } + + close(fd); + + // Record for the snapshot + err = stat(filename.string(), &st); + if (err != 0) { + LOGW("Error stating file that we just created %s", filename.string()); + return errno; + } + + r.file = filename; + r.deleted = false; + r.s.modTime_sec = st.st_mtime; + r.s.modTime_nsec = 0; // workaround sim breakage + //r.s.modTime_nsec = st.st_mtime_nsec; + r.s.size = st.st_size; + r.s.crc32 = crc; + + m_files.add(key, r); + + return NO_ERROR; +} + +status_t +RestoreHelperBase::WriteSnapshot(int fd) +{ + return write_snapshot_file(fd, m_files);; +} + #if TEST_BACKUP_HELPERS #define SCRATCH_DIR "/data/backup_helper_test/" @@ -560,7 +620,6 @@ backup_helper_test_four() FileState states[4]; FileRec r; r.deleted = false; - r.file = NULL; states[0].modTime_sec = 0xfedcba98; states[0].modTime_nsec = 0xdeadbeef; @@ -689,41 +748,27 @@ backup_helper_test_four() // hexdump -v -e '" " 8/1 " 0x%02x," "\n"' data_writer.data const unsigned char DATA_GOLDEN_FILE[] = { - 0x41, 0x70, 0x70, 0x31, 0x0b, 0x00, 0x00, 0x00, - 0xdd, 0xcc, 0xbb, 0xaa, 0x6e, 0x6f, 0x5f, 0x70, - 0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x00, 0x44, 0x61, 0x74, 0x61, 0x0b, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x6e, 0x6f, 0x5f, 0x70, 0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, 0x5f, 0x00, 0x6e, 0x6f, 0x5f, 0x70, 0x61, 0x64, 0x64, 0x69, - 0x6e, 0x67, 0x5f, 0x00, 0x41, 0x70, 0x70, 0x31, - 0x0c, 0x00, 0x00, 0x00, 0xdd, 0xcc, 0xbb, 0xaa, + 0x6e, 0x67, 0x5f, 0x00, 0x44, 0x61, 0x74, 0x61, + 0x0c, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x70, 0x61, 0x64, 0x64, 0x65, 0x64, 0x5f, 0x74, 0x6f, 0x5f, 0x5f, 0x33, 0x00, 0xbc, 0xbc, 0xbc, - 0x44, 0x61, 0x74, 0x61, 0x0c, 0x00, 0x00, 0x00, - 0x0d, 0x00, 0x00, 0x00, 0x70, 0x61, 0x64, 0x64, - 0x65, 0x64, 0x5f, 0x74, 0x6f, 0x5f, 0x5f, 0x33, - 0x00, 0xbc, 0xbc, 0xbc, 0x70, 0x61, 0x64, 0x64, - 0x65, 0x64, 0x5f, 0x74, 0x6f, 0x5f, 0x5f, 0x33, - 0x00, 0xbc, 0xbc, 0xbc, 0x41, 0x70, 0x70, 0x31, - 0x0d, 0x00, 0x00, 0x00, 0xdd, 0xcc, 0xbb, 0xaa, 0x70, 0x61, 0x64, 0x64, 0x65, 0x64, 0x5f, 0x74, - 0x6f, 0x5f, 0x32, 0x5f, 0x5f, 0x00, 0xbc, 0xbc, + 0x6f, 0x5f, 0x5f, 0x33, 0x00, 0xbc, 0xbc, 0xbc, 0x44, 0x61, 0x74, 0x61, 0x0d, 0x00, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x70, 0x61, 0x64, 0x64, 0x65, 0x64, 0x5f, 0x74, 0x6f, 0x5f, 0x32, 0x5f, 0x5f, 0x00, 0xbc, 0xbc, 0x70, 0x61, 0x64, 0x64, 0x65, 0x64, 0x5f, 0x74, 0x6f, 0x5f, 0x32, 0x5f, - 0x5f, 0x00, 0xbc, 0xbc, 0x41, 0x70, 0x70, 0x31, - 0x0a, 0x00, 0x00, 0x00, 0xdd, 0xcc, 0xbb, 0xaa, - 0x70, 0x61, 0x64, 0x64, 0x65, 0x64, 0x5f, 0x74, - 0x6f, 0x31, 0x00, 0xbc, 0x44, 0x61, 0x74, 0x61, + 0x5f, 0x00, 0xbc, 0xbc, 0x44, 0x61, 0x74, 0x61, 0x0a, 0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x00, 0x70, 0x61, 0x64, 0x64, 0x65, 0x64, 0x5f, 0x74, 0x6f, 0x31, 0x00, 0xbc, 0x70, 0x61, 0x64, 0x64, - 0x65, 0x64, 0x5f, 0x74, 0x6f, 0x31, 0x00, 0xbc, - 0x46, 0x6f, 0x6f, 0x74, 0x04, 0x00, 0x00, 0x00, - 0x99, 0x99, 0x77, 0x77 + 0x65, 0x64, 0x5f, 0x74, 0x6f, 0x31, 0x00 + }; const int DATA_GOLDEN_FILE_SIZE = sizeof(DATA_GOLDEN_FILE); @@ -733,12 +778,6 @@ test_write_header_and_entity(BackupDataWriter& writer, const char* str) int err; String8 text(str); - err = writer.WriteAppHeader(text, 0xaabbccdd); - if (err != 0) { - fprintf(stderr, "WriteAppHeader failed with %s\n", strerror(err)); - return err; - } - err = writer.WriteEntityHeader(text, text.length()+1); if (err != 0) { fprintf(stderr, "WriteEntityHeader failed with %s\n", strerror(err)); @@ -779,8 +818,6 @@ backup_helper_test_data_writer() err |= test_write_header_and_entity(writer, "padded_to_2__"); err |= test_write_header_and_entity(writer, "padded_to1"); - writer.WriteAppFooter(0x77779999); - close(fd); err = compare_file(filename, DATA_GOLDEN_FILE, DATA_GOLDEN_FILE_SIZE); @@ -800,70 +837,59 @@ test_read_header_and_entity(BackupDataReader& reader, const char* str) String8 string; int cookie = 0x11111111; size_t actualSize; + bool done; + int type; // printf("\n\n---------- test_read_header_and_entity -- %s\n\n", str); - err = reader.ReadNextHeader(); - if (err != 0) { - fprintf(stderr, "ReadNextHeader (for app header) failed with %s\n", strerror(err)); - goto done; + err = reader.ReadNextHeader(&done, &type); + if (done) { + fprintf(stderr, "should not be done yet\n"); + goto finished; } - - err = reader.ReadAppHeader(&string, &cookie); if (err != 0) { - fprintf(stderr, "ReadAppHeader failed with %s\n", strerror(err)); - goto done; - } - if (string != str) { - fprintf(stderr, "ReadAppHeader expected packageName '%s' got '%s'\n", str, string.string()); - err = EINVAL; - goto done; + fprintf(stderr, "ReadNextHeader (for app header) failed with %s\n", strerror(err)); + goto finished; } - if (cookie != (int)0xaabbccdd) { - fprintf(stderr, "ReadAppHeader expected cookie 0x%08x got 0x%08x\n", 0xaabbccdd, cookie); + if (type != BACKUP_HEADER_ENTITY_V1) { err = EINVAL; - goto done; - } - - err = reader.ReadNextHeader(); - if (err != 0) { - fprintf(stderr, "ReadNextHeader (for entity header) failed with %s\n", strerror(err)); - goto done; + fprintf(stderr, "type=0x%08x expected 0x%08x\n", type, BACKUP_HEADER_ENTITY_V1); } err = reader.ReadEntityHeader(&string, &actualSize); if (err != 0) { fprintf(stderr, "ReadEntityHeader failed with %s\n", strerror(err)); - goto done; + goto finished; } if (string != str) { fprintf(stderr, "ReadEntityHeader expected key '%s' got '%s'\n", str, string.string()); err = EINVAL; - goto done; + goto finished; } if ((int)actualSize != bufSize) { fprintf(stderr, "ReadEntityHeader expected dataSize 0x%08x got 0x%08x\n", bufSize, actualSize); err = EINVAL; - goto done; + goto finished; } err = reader.ReadEntityData(buf, bufSize); if (err != NO_ERROR) { fprintf(stderr, "ReadEntityData failed with %s\n", strerror(err)); - goto done; + goto finished; } if (0 != memcmp(buf, str, bufSize)) { fprintf(stderr, "ReadEntityData expected '%s' but got something starting with " - "%02x %02x %02x %02x\n", str, buf[0], buf[1], buf[2], buf[3]); + "%02x %02x %02x %02x '%c%c%c%c'\n", str, buf[0], buf[1], buf[2], buf[3], + buf[0], buf[1], buf[2], buf[3]); err = EINVAL; - goto done; + goto finished; } // The next read will confirm whether it got the right amount of data. -done: +finished: if (err != NO_ERROR) { fprintf(stderr, "test_read_header_and_entity failed with %s\n", strerror(err)); } @@ -923,23 +949,6 @@ backup_helper_test_data_reader() if (err == NO_ERROR) { err = test_read_header_and_entity(reader, "padded_to1"); } - - if (err == NO_ERROR) { - err = reader.ReadNextHeader(); - if (err != 0) { - fprintf(stderr, "ReadNextHeader (for app header) failed with %s\n", strerror(err)); - } - - if (err == NO_ERROR) { - int cookie; - err |= reader.ReadAppFooter(&cookie); - if (cookie != 0x77779999) { - fprintf(stderr, "app footer cookie expected=0x%08x actual=0x%08x\n", - 0x77779999, cookie); - err = EINVAL; - } - } - } } close(fd); diff --git a/libs/utils/ResourceTypes.cpp b/libs/utils/ResourceTypes.cpp index 3d12dca..69d47f0 100644 --- a/libs/utils/ResourceTypes.cpp +++ b/libs/utils/ResourceTypes.cpp @@ -3830,9 +3830,45 @@ status_t ResTable::parsePackage(const ResTable_package* const pkg, #define CHAR16_ARRAY_EQ(constant, var, len) \ ((len == (sizeof(constant)/sizeof(constant[0]))) && (0 == memcmp((var), (constant), (len)))) -void ResTable::print() const +void print_complex(uint32_t complex, bool isFraction) +{ + const float MANTISSA_MULT = + 1.0f / (1<<Res_value::COMPLEX_MANTISSA_SHIFT); + const float RADIX_MULTS[] = { + 1.0f*MANTISSA_MULT, 1.0f/(1<<7)*MANTISSA_MULT, + 1.0f/(1<<15)*MANTISSA_MULT, 1.0f/(1<<23)*MANTISSA_MULT + }; + + float value = (complex&(Res_value::COMPLEX_MANTISSA_MASK + <<Res_value::COMPLEX_MANTISSA_SHIFT)) + * RADIX_MULTS[(complex>>Res_value::COMPLEX_RADIX_SHIFT) + & Res_value::COMPLEX_RADIX_MASK]; + printf("%f", value); + + if (isFraction) { + switch ((complex>>Res_value::COMPLEX_UNIT_SHIFT)&Res_value::COMPLEX_UNIT_MASK) { + case Res_value::COMPLEX_UNIT_PX: printf("px"); break; + case Res_value::COMPLEX_UNIT_DIP: printf("dp"); break; + case Res_value::COMPLEX_UNIT_SP: printf("sp"); break; + case Res_value::COMPLEX_UNIT_PT: printf("pt"); break; + case Res_value::COMPLEX_UNIT_IN: printf("in"); break; + case Res_value::COMPLEX_UNIT_MM: printf("mm"); break; + default: printf(" (unknown unit)"); break; + } + } else { + switch ((complex>>Res_value::COMPLEX_UNIT_SHIFT)&Res_value::COMPLEX_UNIT_MASK) { + case Res_value::COMPLEX_UNIT_FRACTION: printf("%%"); break; + case Res_value::COMPLEX_UNIT_FRACTION_PARENT: printf("%%p"); break; + default: printf(" (unknown unit)"); break; + } + } +} + +void ResTable::print(bool inclValues) const { - printf("mError=0x%x (%s)\n", mError, strerror(mError)); + if (mError != 0) { + printf("mError=0x%x (%s)\n", mError, strerror(mError)); + } #if 0 printf("mParams=%c%c-%c%c,\n", mParams.language[0], mParams.language[1], @@ -3947,6 +3983,8 @@ void ResTable::print() const (void*)(entriesStart + thisOffset)); continue; } + + const Res_value* value = NULL; if ((dtohs(ent->flags)&ResTable_entry::FLAG_COMPLEX) != 0) { printf("<bag>"); } else { @@ -3962,7 +4000,7 @@ void ResTable::print() const continue; } - const Res_value* value = (const Res_value*) + value = (const Res_value*) (((const uint8_t*)ent) + esize); printf("t=0x%02x d=0x%08x (s=0x%04x r=0x%02x)", (int)value->dataType, (int)dtohl(value->data), @@ -3973,6 +4011,49 @@ void ResTable::print() const printf(" (PUBLIC)"); } printf("\n"); + + if (inclValues) { + if (value != NULL) { + printf(" "); + if (value->dataType == Res_value::TYPE_NULL) { + printf("(null)\n"); + } else if (value->dataType == Res_value::TYPE_REFERENCE) { + printf("(reference) 0x%08x\n", value->data); + } else if (value->dataType == Res_value::TYPE_ATTRIBUTE) { + printf("(attribute) 0x%08x\n", value->data); + } else if (value->dataType == Res_value::TYPE_STRING) { + size_t len; + const char16_t* str = pkg->header->values.stringAt( + value->data, &len); + if (str == NULL) { + printf("(string) null\n"); + } else { + printf("(string) \"%s\"\n", + String8(str, len).string()); + } + } else if (value->dataType == Res_value::TYPE_FLOAT) { + printf("(float) %g\n", *(const float*)&value->data); + } else if (value->dataType == Res_value::TYPE_DIMENSION) { + printf("(dimension) "); + print_complex(value->data, false); + printf("\n"); + } else if (value->dataType == Res_value::TYPE_FRACTION) { + printf("(fraction) "); + print_complex(value->data, true); + printf("\n"); + } else if (value->dataType >= Res_value::TYPE_FIRST_COLOR_INT + || value->dataType <= Res_value::TYPE_LAST_COLOR_INT) { + printf("(color) #%08x\n", value->data); + } else if (value->dataType == Res_value::TYPE_INT_BOOLEAN) { + printf("(boolean) %s\n", value->data ? "true" : "false"); + } else if (value->dataType >= Res_value::TYPE_FIRST_INT + || value->dataType <= Res_value::TYPE_LAST_INT) { + printf("(int) 0x%08x or %d\n", value->data, value->data); + } else { + printf("(unknown type)\n"); + } + } + } } } } diff --git a/location/java/com/android/internal/location/GpsLocationProvider.java b/location/java/com/android/internal/location/GpsLocationProvider.java index 9698553..e4ff0e3 100644..100755 --- a/location/java/com/android/internal/location/GpsLocationProvider.java +++ b/location/java/com/android/internal/location/GpsLocationProvider.java @@ -208,12 +208,6 @@ public class GpsLocationProvider extends ILocationProvider.Stub { private GpsNetworkThread mNetworkThread; private Object mNetworkThreadLock = new Object(); - private String mSuplHost; - private int mSuplPort; - private String mC2KHost; - private int mC2KPort; - private boolean mSetSuplServer; - private boolean mSetC2KServer; private String mAGpsApn; private int mAGpsDataConnectionState; private final ConnectivityManager mConnMgr; @@ -355,23 +349,27 @@ public class GpsLocationProvider extends ILocationProvider.Stub { stream.close(); mNtpServer = mProperties.getProperty("NTP_SERVER", null); - mSuplHost = mProperties.getProperty("SUPL_HOST"); + String host = mProperties.getProperty("SUPL_HOST"); String portString = mProperties.getProperty("SUPL_PORT"); - if (mSuplHost != null && portString != null) { + if (host != null && portString != null) { try { - mSuplPort = Integer.parseInt(portString); - mSetSuplServer = true; + int port = Integer.parseInt(portString); + native_set_agps_server(AGPS_TYPE_SUPL, host, port); + // use MS-Based position mode if SUPL support is enabled + mPositionMode = GPS_POSITION_MODE_MS_BASED; } catch (NumberFormatException e) { Log.e(TAG, "unable to parse SUPL_PORT: " + portString); } } - mC2KHost = mProperties.getProperty("C2K_HOST"); + host = mProperties.getProperty("C2K_HOST"); portString = mProperties.getProperty("C2K_PORT"); - if (mC2KHost != null && portString != null) { + if (host != null && portString != null) { try { - mC2KPort = Integer.parseInt(portString); - mSetC2KServer = true; + int port = Integer.parseInt(portString); + native_set_agps_server(AGPS_TYPE_C2K, host, port); + // use MS-Based position mode if SUPL support is enabled + mPositionMode = GPS_POSITION_MODE_MS_BASED; } catch (NumberFormatException e) { Log.e(TAG, "unable to parse C2K_PORT: " + portString); } @@ -386,10 +384,7 @@ public class GpsLocationProvider extends ILocationProvider.Stub { * data network (e.g., the Internet), false otherwise. */ public boolean requiresNetwork() { - // We want updateNetworkState() to get called when the network state changes - // for XTRA and NTP time injection support. - return (mNtpServer != null || native_supports_xtra() || - mSuplHost != null || mC2KHost != null); + return true; } public void updateNetworkState(int state) { @@ -989,29 +984,6 @@ public class GpsLocationProvider extends ILocationProvider.Stub { } } - private boolean setAGpsServer(int type, String host, int port) { - try { - InetAddress inetAddress = InetAddress.getByName(host); - if (inetAddress != null) { - byte[] addrBytes = inetAddress.getAddress(); - long addr = 0; - for (int i = 0; i < addrBytes.length; i++) { - int temp = addrBytes[i]; - // signed -> unsigned - if (temp < 0) temp = 256 + temp; - addr = addr * 256 + temp; - } - // use MS-Based position mode if SUPL support is enabled - mPositionMode = GPS_POSITION_MODE_MS_BASED; - native_set_agps_server(type, (int)addr, port); - } - } catch (UnknownHostException e) { - Log.e(TAG, "unknown host for server " + host); - return false; - } - return true; - } - private class GpsEventThread extends Thread { public GpsEventThread() { @@ -1085,7 +1057,7 @@ public class GpsLocationProvider extends ILocationProvider.Stub { } waitTime = getWaitTime(); } while (!mDone && ((!mXtraDownloadRequested && - !mTimeInjectRequested && !mSetSuplServer && !mSetC2KServer && waitTime > 0) + !mTimeInjectRequested && waitTime > 0) || !mNetworkAvailable)); if (Config.LOGD) Log.d(TAG, "NetworkThread out of wake loop"); @@ -1113,18 +1085,6 @@ public class GpsLocationProvider extends ILocationProvider.Stub { } } - // Set the AGPS server addresses if we have not yet - if (mSetSuplServer) { - if (setAGpsServer(AGPS_TYPE_SUPL, mSuplHost, mSuplPort)) { - mSetSuplServer = false; - } - } - if (mSetC2KServer) { - if (setAGpsServer(AGPS_TYPE_C2K, mC2KHost, mC2KPort)) { - mSetC2KServer = false; - } - } - if ((mXtraDownloadRequested || (mNextXtraTime > 0 && mNextXtraTime <= System.currentTimeMillis())) && xtraDownloader != null) { @@ -1225,5 +1185,5 @@ public class GpsLocationProvider extends ILocationProvider.Stub { private native void native_agps_data_conn_open(String apn); private native void native_agps_data_conn_closed(); private native void native_agps_data_conn_failed(); - private native void native_set_agps_server(int type, int addr, int port); + private native void native_set_agps_server(int type, String hostname, int port); } diff --git a/packages/SettingsProvider/AndroidManifest.xml b/packages/SettingsProvider/AndroidManifest.xml index 16c66a6..2407d87 100644 --- a/packages/SettingsProvider/AndroidManifest.xml +++ b/packages/SettingsProvider/AndroidManifest.xml @@ -3,7 +3,7 @@ android:sharedUserId="android.uid.system"> <application android:allowClearUserData="false" - android:label="Settings Storage" + android:label="@string/app_label" android:icon="@drawable/ic_launcher_settings"> <provider android:name="SettingsProvider" android:authorities="settings" diff --git a/packages/SettingsProvider/res/values/strings.xml b/packages/SettingsProvider/res/values/strings.xml new file mode 100644 index 0000000..9ca575e --- /dev/null +++ b/packages/SettingsProvider/res/values/strings.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * 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. + */ +--> +<resources> + <!-- Name of the activity for Settings storage. --> + <string name="app_label">Settings Storage</string> +</resources> diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java index 6d90001..1a03900 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java @@ -158,14 +158,18 @@ public class SettingsProvider extends ContentProvider { getContext().checkCallingOrSelfPermission( android.Manifest.permission.WRITE_SECURE_SETTINGS) != PackageManager.PERMISSION_GRANTED) { - throw new SecurityException("Cannot write secure settings table"); - + throw new SecurityException( + String.format("Permission denial: writing to secure settings requires %1$s", + android.Manifest.permission.WRITE_SECURE_SETTINGS)); + // TODO: Move gservices into its own provider so we don't need this nonsense. } else if ("gservices".equals(args.table) && getContext().checkCallingOrSelfPermission( android.Manifest.permission.WRITE_GSERVICES) != PackageManager.PERMISSION_GRANTED) { - throw new SecurityException("Cannot write gservices table"); + throw new SecurityException( + String.format("Permission denial: writing to gservices settings requires %1$s", + android.Manifest.permission.WRITE_GSERVICES)); } } diff --git a/packages/SubscribedFeedsProvider/AndroidManifest.xml b/packages/SubscribedFeedsProvider/AndroidManifest.xml index c254d93..d839c4e 100644 --- a/packages/SubscribedFeedsProvider/AndroidManifest.xml +++ b/packages/SubscribedFeedsProvider/AndroidManifest.xml @@ -10,7 +10,7 @@ <application android:process="system" android:allowClearUserData="false" android:icon="@drawable/app_icon" - android:label="Sync Feeds"> + android:label="@string/app_label"> <uses-library android:name="com.google.android.gtalkservice" /> <provider android:name="SubscribedFeedsProvider" android:authorities="subscribedfeeds" android:syncable="false" diff --git a/packages/SubscribedFeedsProvider/res/values/strings.xml b/packages/SubscribedFeedsProvider/res/values/strings.xml new file mode 100644 index 0000000..072571d --- /dev/null +++ b/packages/SubscribedFeedsProvider/res/values/strings.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2008 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. +--> + +<resources> + <!-- Title of the feed synchronization activity. --> + <string name="app_label">Sync Feeds</string> +</resources> + diff --git a/packages/TtsService/jni/android_tts_SynthProxy.cpp b/packages/TtsService/jni/android_tts_SynthProxy.cpp index 582e6219..54d038a 100644 --- a/packages/TtsService/jni/android_tts_SynthProxy.cpp +++ b/packages/TtsService/jni/android_tts_SynthProxy.cpp @@ -272,7 +272,7 @@ android_tts_SynthProxy_native_finalize(JNIEnv *env, jobject thiz, jint jniData) static void android_tts_SynthProxy_setLanguage(JNIEnv *env, jobject thiz, jint jniData, - jstring language) + jstring language, jstring country, jstring variant) { if (jniData == 0) { LOGE("android_tts_SynthProxy_setLanguage(): invalid JNI data"); @@ -281,12 +281,16 @@ android_tts_SynthProxy_setLanguage(JNIEnv *env, jobject thiz, jint jniData, SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData; const char *langNativeString = env->GetStringUTFChars(language, 0); + const char *countryNativeString = env->GetStringUTFChars(country, 0); + const char *variantNativeString = env->GetStringUTFChars(variant, 0); // TODO check return codes if (pSynthData->mNativeSynthInterface) { - pSynthData->mNativeSynthInterface->setLanguage(langNativeString, - strlen(langNativeString)); + pSynthData->mNativeSynthInterface->setLanguage(langNativeString, countryNativeString, + variantNativeString); } env->ReleaseStringUTFChars(language, langNativeString); + env->ReleaseStringUTFChars(language, countryNativeString); + env->ReleaseStringUTFChars(language, variantNativeString); } @@ -494,6 +498,7 @@ android_tts_SynthProxy_getLanguage(JNIEnv *env, jobject thiz, jint jniData) return env->NewStringUTF(buf); } + JNIEXPORT int JNICALL android_tts_SynthProxy_getRate(JNIEnv *env, jobject thiz, jint jniData) { @@ -529,7 +534,7 @@ static JNINativeMethod gMethods[] = { (void*)android_tts_SynthProxy_synthesizeToFile }, { "native_setLanguage", - "(ILjava/lang/String;)V", + "(ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)V", (void*)android_tts_SynthProxy_setLanguage }, { "native_setSpeechRate", @@ -565,7 +570,6 @@ static JNINativeMethod gMethods[] = { #define SP_JNIDATA_FIELD_NAME "mJniData" #define SP_POSTSPEECHSYNTHESIZED_METHOD_NAME "postNativeSpeechSynthesizedInJava" -// TODO: verify this is the correct path static const char* const kClassPathName = "android/tts/SynthProxy"; jint JNI_OnLoad(JavaVM* vm, void* reserved) diff --git a/packages/TtsService/src/android/tts/SynthProxy.java b/packages/TtsService/src/android/tts/SynthProxy.java index e065f40..364dc58 100755 --- a/packages/TtsService/src/android/tts/SynthProxy.java +++ b/packages/TtsService/src/android/tts/SynthProxy.java @@ -70,8 +70,8 @@ public class SynthProxy { /** * Sets the language */ - public void setLanguage(String language) { - native_setLanguage(mJniData, language); + public void setLanguage(String language, String country, String variant) { + native_setLanguage(mJniData, language, country, variant); } /** @@ -141,7 +141,8 @@ public class SynthProxy { private native final void native_synthesizeToFile(int jniData, String text, String filename); - private native final void native_setLanguage(int jniData, String language); + private native final void native_setLanguage(int jniData, String language, String country, + String variant); private native final void native_setSpeechRate(int jniData, int speechRate); diff --git a/packages/TtsService/src/android/tts/TtsService.java b/packages/TtsService/src/android/tts/TtsService.java index 8a64113..e9c4ab7 100755 --- a/packages/TtsService/src/android/tts/TtsService.java +++ b/packages/TtsService/src/android/tts/TtsService.java @@ -15,10 +15,8 @@ */ package android.tts; -import android.speech.tts.ITts.Stub; -import android.speech.tts.ITtsCallback; - import android.app.Service; +import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; @@ -31,6 +29,9 @@ import android.os.IBinder; import android.os.RemoteCallbackList; import android.os.RemoteException; import android.preference.PreferenceManager; +import android.speech.tts.ITts.Stub; +import android.speech.tts.ITtsCallback; +import android.speech.tts.TextToSpeech; import android.util.Log; import java.util.ArrayList; import java.util.Arrays; @@ -101,7 +102,7 @@ public class TtsService extends Service implements OnCompletionListener { private MediaPlayer mPlayer; private TtsService mSelf; - private SharedPreferences prefs; + private ContentResolver mResolver; private final ReentrantLock speechQueueLock = new ReentrantLock(); private final ReentrantLock synthesizerLock = new ReentrantLock(); @@ -113,9 +114,7 @@ public class TtsService extends Service implements OnCompletionListener { super.onCreate(); Log.i("TTS", "TTS starting"); - // TODO: Make this work when the settings are done in the main Settings - // app. - prefs = PreferenceManager.getDefaultSharedPreferences(this); + mResolver = getContentResolver(); String soLibPath = "/system/lib/libttspico.so"; nativeSynth = new SynthProxy(soLibPath); @@ -129,8 +128,7 @@ public class TtsService extends Service implements OnCompletionListener { mSpeechQueue = new ArrayList<SpeechItem>(); mPlayer = null; - setLanguage(prefs.getString("lang_pref", "en-rUS")); - setSpeechRate(Integer.parseInt(prefs.getString("rate_pref", "140"))); + setDefaultSettings(); } @Override @@ -145,25 +143,50 @@ public class TtsService extends Service implements OnCompletionListener { mCallbacks.kill(); } + + private void setDefaultSettings() { + + // TODO handle default language + setLanguage("eng", "USA", ""); + + // speech rate + setSpeechRate(getDefaultRate()); + + // TODO handle default pitch + } + + + private boolean isDefaultEnforced() { + return (android.provider.Settings.Secure.getInt(mResolver, + android.provider.Settings.Secure.TTS_USE_DEFAULTS, + TextToSpeech.Engine.FALLBACK_TTS_USE_DEFAULTS) + == 1 ); + } + + + private int getDefaultRate() { + return android.provider.Settings.Secure.getInt(mResolver, + android.provider.Settings.Secure.TTS_DEFAULT_RATE, + TextToSpeech.Engine.FALLBACK_TTS_DEFAULT_RATE); + } + + private void setSpeechRate(int rate) { - if (prefs.getBoolean("override_pref", false)) { - // This is set to the default here so that the preview in the prefs - // activity will show the change without a restart, even if apps are - // not allowed to change the defaults. - rate = Integer.parseInt(prefs.getString("rate_pref", "140")); + if (isDefaultEnforced()) { + nativeSynth.setSpeechRate(getDefaultRate()); + } else { + nativeSynth.setSpeechRate(rate); } - nativeSynth.setSpeechRate(rate); } - private void setLanguage(String lang) { - if (prefs.getBoolean("override_pref", false)) { - // This is set to the default here so that the preview in the prefs - // activity will show the change without a restart, even if apps are - // not - // allowed to change the defaults. - lang = prefs.getString("lang_pref", "en-rUS"); + private void setLanguage(String lang, String country, String variant) { + Log.v("TTS", "TtsService.setLanguage("+lang+", "+country+", "+variant+")"); + if (isDefaultEnforced()) { + nativeSynth.setLanguage(lang, country, variant); + } else { + // TODO handle default language + nativeSynth.setLanguage("eng", "USA", ""); } - nativeSynth.setLanguage(lang); } /** @@ -683,20 +706,14 @@ public class TtsService extends Service implements OnCompletionListener { } /** - * Sets the speech rate for the TTS. Note that this will only have an - * effect on synthesized speech; it will not affect pre-recorded speech. + * Sets the speech rate for the TTS, which affects the synthesized voice. * - * @param language - * Language values are based on the Android conventions for - * localization as described in the Android platform - * documentation on internationalization. This implies that - * language data is specified in the format xx-rYY, where xx - * is a two letter ISO 639-1 language code in lowercase and - * rYY is a two letter ISO 3166-1-alpha-2 language code in - * uppercase preceded by a lowercase "r". + * @param lang the three letter ISO language code. + * @param country the three letter ISO country code. + * @param variant the variant code associated with the country and language pair. */ - public void setLanguage(String language) { - mSelf.setLanguage(language); + public void setLanguage(String lang, String country, String variant) { + mSelf.setLanguage(lang, country, variant); } /** diff --git a/packages/VpnServices/AndroidManifest.xml b/packages/VpnServices/AndroidManifest.xml index e48b2da..6092e30 100644 --- a/packages/VpnServices/AndroidManifest.xml +++ b/packages/VpnServices/AndroidManifest.xml @@ -3,7 +3,7 @@ package="com.android.server.vpn" android:sharedUserId="android.uid.system" > - <application android:label="VPN Services"> + <application android:label="@string/app_label"> <service android:name=".VpnServiceBinder" android:process=":remote"> <intent-filter> diff --git a/packages/VpnServices/res/values/strings.xml b/packages/VpnServices/res/values/strings.xml index 892850a..e42c1e8 100755 --- a/packages/VpnServices/res/values/strings.xml +++ b/packages/VpnServices/res/values/strings.xml @@ -1,5 +1,8 @@ <?xml version="1.0" encoding="utf-8"?> <resources> + <!-- Title for the VPN Services activity. --> + <string name="app_label">VPN Services</string> + <string name="vpn_notification_title">%s VPN %s</string> <string name="vpn_notification_connected">connected</string> <string name="vpn_notification_disconnected">disconnected</string> diff --git a/packages/VpnServices/src/com/android/server/vpn/L2tpIpsecService.java b/packages/VpnServices/src/com/android/server/vpn/L2tpIpsecService.java index ce56921..8358c5c 100644 --- a/packages/VpnServices/src/com/android/server/vpn/L2tpIpsecService.java +++ b/packages/VpnServices/src/com/android/server/vpn/L2tpIpsecService.java @@ -50,16 +50,17 @@ class L2tpIpsecService extends VpnService<L2tpIpsecProfile> { } private String getCaCertPath() { - return Keystore.getInstance().getCertificate( + return Keystore.getInstance().getCaCertificate( getProfile().getCaCertificate()); } private String getUserCertPath() { - return Keystore.getInstance().getCertificate( + return Keystore.getInstance().getUserCertificate( getProfile().getUserCertificate()); } private String getUserkeyPath() { - return Keystore.getInstance().getUserkey(getProfile().getUserkey()); + return Keystore.getInstance().getUserPrivateKey( + getProfile().getUserCertificate()); } } diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java index 2f964ba..75e0e64 100644 --- a/services/java/com/android/server/BackupManagerService.java +++ b/services/java/com/android/server/BackupManagerService.java @@ -21,14 +21,17 @@ import android.app.IActivityManager; import android.app.IApplicationThread; import android.app.IBackupAgent; import android.content.BroadcastReceiver; +import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.ServiceConnection; import android.content.pm.ApplicationInfo; import android.content.pm.IPackageDataObserver; import android.content.pm.PackageInfo; -import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; +import android.content.pm.PackageManager; +import android.content.pm.Signature; import android.net.Uri; import android.os.Binder; import android.os.Bundle; @@ -48,9 +51,11 @@ import android.backup.BackupManager; import android.backup.RestoreSet; import com.android.internal.backup.LocalTransport; -import com.android.internal.backup.GoogleTransport; import com.android.internal.backup.IBackupTransport; +import com.android.server.PackageManagerBackupAgent; +import com.android.server.PackageManagerBackupAgent.Metadata; + import java.io.EOFException; import java.io.File; import java.io.FileDescriptor; @@ -68,7 +73,8 @@ import java.util.List; class BackupManagerService extends IBackupManager.Stub { private static final String TAG = "BackupManagerService"; private static final boolean DEBUG = true; - + + // Default time to wait after data changes before we back up the data private static final long COLLECTION_INTERVAL = 1000; //private static final long COLLECTION_INTERVAL = 3 * 60 * 1000; @@ -90,7 +96,7 @@ class BackupManagerService extends IBackupManager.Stub { private class BackupRequest { public ApplicationInfo appInfo; public boolean fullBackup; - + BackupRequest(ApplicationInfo app, boolean isFull) { appInfo = app; fullBackup = isFull; @@ -103,9 +109,11 @@ class BackupManagerService extends IBackupManager.Stub { // Backups that we haven't started yet. private HashMap<ApplicationInfo,BackupRequest> mPendingBackups = new HashMap<ApplicationInfo,BackupRequest>(); - // Backups that we have started. These are separate to prevent starvation - // if an app keeps re-enqueuing itself. - private ArrayList<BackupRequest> mBackupQueue; + // Do we need to back up the package manager metadata on the next pass? + private boolean mDoPackageManager; + private static final String PACKAGE_MANAGER_SENTINEL = "@pm@"; + + // locking around the pending-backup management private final Object mQueueLock = new Object(); // The thread performing the sequence of queued backups binds to each app's agent @@ -120,14 +128,17 @@ class BackupManagerService extends IBackupManager.Stub { private final Object mClearDataLock = new Object(); private volatile boolean mClearingData; + // Current active transport & restore session private int mTransportId; + private IBackupTransport mLocalTransport, mGoogleTransport; + private RestoreSession mActiveRestoreSession; private File mStateDir; private File mDataDir; private File mJournalDir; private File mJournal; private RandomAccessFile mJournalStream; - + public BackupManagerService(Context context) { mContext = context; mPackageManager = context.getPackageManager(); @@ -143,14 +154,27 @@ class BackupManagerService extends IBackupManager.Stub { mJournalDir.mkdirs(); makeJournalLocked(); // okay because no other threads are running yet - //!!! TODO: default to cloud transport, not local - mTransportId = BackupManager.TRANSPORT_LOCAL; - - // Build our mapping of uid to backup client services + // Build our mapping of uid to backup client services. This implicitly + // schedules a backup pass on the Package Manager metadata the first + // time anything needs to be backed up. synchronized (mBackupParticipants) { addPackageParticipantsLocked(null); } + // Set up our transport options and initialize the default transport + // TODO: Have transports register themselves somehow? + // TODO: Don't create transports that we don't need to? + //mTransportId = BackupManager.TRANSPORT_LOCAL; + mTransportId = BackupManager.TRANSPORT_GOOGLE; + mLocalTransport = new LocalTransport(context); // This is actually pretty cheap + mGoogleTransport = null; + + // Attach to the Google backup transport. + Intent intent = new Intent().setComponent(new ComponentName( + "com.google.android.backup", + "com.google.android.backup.BackupTransportService")); + context.bindService(intent, mGoogleConnection, Context.BIND_AUTO_CREATE); + // Now that we know about valid backup participants, parse any // leftover journal files and schedule a new backup pass parseLeftoverJournals(); @@ -244,6 +268,19 @@ class BackupManagerService extends IBackupManager.Stub { } }; + // ----- Track connection to GoogleBackupTransport service ----- + ServiceConnection mGoogleConnection = new ServiceConnection() { + public void onServiceConnected(ComponentName name, IBinder service) { + if (DEBUG) Log.v(TAG, "Connected to Google transport"); + mGoogleTransport = IBackupTransport.Stub.asInterface(service); + } + + public void onServiceDisconnected(ComponentName name) { + if (DEBUG) Log.v(TAG, "Disconnected from Google transport"); + mGoogleTransport = null; + } + }; + // ----- Run the actual backup process asynchronously ----- private class BackupHandler extends Handler { @@ -251,7 +288,15 @@ class BackupManagerService extends IBackupManager.Stub { switch (msg.what) { case MSG_RUN_BACKUP: + { + IBackupTransport transport = getTransport(mTransportId); + if (transport == null) { + Log.v(TAG, "Backup requested but no transport available"); + break; + } + // snapshot the pending-backup set and work on that + ArrayList<BackupRequest> queue = new ArrayList<BackupRequest>(); File oldJournal = mJournal; synchronized (mQueueLock) { if (mPendingBackups.size() == 0) { @@ -259,13 +304,11 @@ class BackupManagerService extends IBackupManager.Stub { break; } - if (mBackupQueue == null) { - mBackupQueue = new ArrayList<BackupRequest>(); - for (BackupRequest b: mPendingBackups.values()) { - mBackupQueue.add(b); - } - mPendingBackups = new HashMap<ApplicationInfo,BackupRequest>(); + for (BackupRequest b: mPendingBackups.values()) { + queue.add(b); } + Log.v(TAG, "clearing pending backups"); + mPendingBackups.clear(); // Start a new backup-queue journal file too if (mJournalStream != null) { @@ -283,8 +326,10 @@ class BackupManagerService extends IBackupManager.Stub { // deleted. If we crash prior to that, the old journal is parsed // at next boot and the journaled requests fulfilled. } - (new PerformBackupThread(mTransportId, mBackupQueue, oldJournal)).run(); + + (new PerformBackupThread(transport, queue, oldJournal)).start(); break; + } case MSG_RUN_FULL_BACKUP: break; @@ -293,7 +338,7 @@ class BackupManagerService extends IBackupManager.Stub { { int token = msg.arg1; IBackupTransport transport = (IBackupTransport)msg.obj; - (new PerformRestoreThread(transport, token)).run(); + (new PerformRestoreThread(transport, token)).start(); break; } } @@ -327,12 +372,13 @@ class BackupManagerService extends IBackupManager.Stub { mBackupParticipants.put(uid, set); } set.add(app); + backUpPackageManagerData(); } } } - // Remove the given package's backup services from our known active set. If - // 'packageName' is null, *all* backup services will be removed. + // Remove the given package's entry from our known active set. If + // 'packageName' is null, *all* participating apps will be removed. void removePackageParticipantsLocked(String packageName) { if (DEBUG) Log.v(TAG, "removePackageParticipantsLocked: " + packageName); List<ApplicationInfo> allApps = null; @@ -370,6 +416,7 @@ class BackupManagerService extends IBackupManager.Stub { for (ApplicationInfo entry: set) { if (entry.packageName.equals(app.packageName)) { set.remove(entry); + backUpPackageManagerData(); break; } } @@ -382,6 +429,7 @@ class BackupManagerService extends IBackupManager.Stub { // Returns the set of all applications that define an android:backupAgent attribute private List<ApplicationInfo> allAgentApps() { + // !!! TODO: cache this and regenerate only when necessary List<ApplicationInfo> allApps = mPackageManager.getInstalledApplications(0); int N = allApps.size(); if (N > 0) { @@ -395,7 +443,7 @@ class BackupManagerService extends IBackupManager.Stub { } return allApps; } - + // Reset the given package's known backup participants. Unlike add/remove, the update // action cannot be passed a null package name. void updatePackageParticipantsLocked(String packageName) { @@ -411,25 +459,35 @@ class BackupManagerService extends IBackupManager.Stub { addPackageParticipantsLockedInner(packageName, allApps); } - // Instantiate the given transport - private IBackupTransport createTransport(int transportID) { - IBackupTransport transport = null; + private void backUpPackageManagerData() { + // No need to schedule a backup just for the metadata; just piggyback on + // the next actual data backup. + synchronized(this) { + mDoPackageManager = true; + } + } + + // The queue lock should be held when scheduling a backup pass + private void scheduleBackupPassLocked(long timeFromNowMillis) { + mBackupHandler.removeMessages(MSG_RUN_BACKUP); + mBackupHandler.sendEmptyMessageDelayed(MSG_RUN_BACKUP, timeFromNowMillis); + } + + // Return the given transport + private IBackupTransport getTransport(int transportID) { switch (transportID) { case BackupManager.TRANSPORT_LOCAL: - if (DEBUG) Log.v(TAG, "Initializing local transport"); - transport = new LocalTransport(mContext); - break; + Log.v(TAG, "Supplying local transport"); + return mLocalTransport; case BackupManager.TRANSPORT_GOOGLE: - if (DEBUG) Log.v(TAG, "Initializing Google transport"); - //!!! TODO: stand up the google backup transport for real here - transport = new GoogleTransport(); - break; + Log.v(TAG, "Supplying Google transport"); + return mGoogleTransport; default: - Log.e(TAG, "creating unknown transport " + transportID); + Log.e(TAG, "Asked for unknown transport " + transportID); + return null; } - return transport; } // fire off a backup agent, blocking until it attaches or times out @@ -495,7 +553,7 @@ class BackupManagerService extends IBackupManager.Stub { throws android.os.RemoteException { synchronized(mClearDataLock) { mClearingData = false; - notifyAll(); + mClearDataLock.notifyAll(); } } } @@ -504,13 +562,13 @@ class BackupManagerService extends IBackupManager.Stub { class PerformBackupThread extends Thread { private static final String TAG = "PerformBackupThread"; - int mTransport; + IBackupTransport mTransport; ArrayList<BackupRequest> mQueue; File mJournal; - public PerformBackupThread(int transportId, ArrayList<BackupRequest> queue, + public PerformBackupThread(IBackupTransport transport, ArrayList<BackupRequest> queue, File journal) { - mTransport = transportId; + mTransport = transport; mQueue = queue; mJournal = journal; } @@ -519,27 +577,43 @@ class BackupManagerService extends IBackupManager.Stub { public void run() { if (DEBUG) Log.v(TAG, "Beginning backup of " + mQueue.size() + " targets"); - // stand up the current transport - IBackupTransport transport = createTransport(mTransport); - if (transport == null) { - return; - } - // start up the transport try { - transport.startSession(); + mTransport.startSession(); } catch (Exception e) { Log.e(TAG, "Error session transport"); e.printStackTrace(); return; } - // The transport is up and running; now run all the backups in our queue - doQueuedBackups(transport); + // The transport is up and running. First, back up the package manager + // metadata if necessary + boolean doPackageManager; + synchronized (BackupManagerService.this) { + doPackageManager = mDoPackageManager; + mDoPackageManager = false; + } + if (doPackageManager) { + // The package manager doesn't have a proper <application> etc, but since + // it's running here in the system process we can just set up its agent + // directly and use a synthetic BackupRequest. + if (DEBUG) Log.i(TAG, "Running PM backup pass as well"); + + PackageManagerBackupAgent pmAgent = new PackageManagerBackupAgent( + mPackageManager, allAgentApps()); + BackupRequest pmRequest = new BackupRequest(new ApplicationInfo(), false); + pmRequest.appInfo.packageName = PACKAGE_MANAGER_SENTINEL; + processOneBackup(pmRequest, + IBackupAgent.Stub.asInterface(pmAgent.onBind()), + mTransport); + } + + // Now run all the backups in our queue + doQueuedBackups(mTransport); // Finally, tear down the transport try { - transport.endSession(); + mTransport.endSession(); } catch (Exception e) { Log.e(TAG, "Error ending transport"); e.printStackTrace(); @@ -585,8 +659,15 @@ class BackupManagerService extends IBackupManager.Stub { // Look up the package info & signatures. This is first so that if it // throws an exception, there's no file setup yet that would need to // be unraveled. - PackageInfo packInfo = mPackageManager.getPackageInfo(packageName, + PackageInfo packInfo; + if (packageName.equals(PACKAGE_MANAGER_SENTINEL)) { + // The metadata 'package' is synthetic + packInfo = new PackageInfo(); + packInfo.packageName = packageName; + } else { + packInfo = mPackageManager.getPackageInfo(packageName, PackageManager.GET_SIGNATURES); + } // !!! TODO: get the state file dir from the transport File savedStateName = new File(mStateDir, packageName); @@ -674,6 +755,40 @@ class BackupManagerService extends IBackupManager.Stub { return null; } + private boolean signaturesMatch(Signature[] storedSigs, Signature[] deviceSigs) { + // Allow unsigned apps, but not signed on one device and unsigned on the other + // !!! TODO: is this the right policy? + if (DEBUG) Log.v(TAG, "signaturesMatch(): stored=" + storedSigs + + " device=" + deviceSigs); + if ((storedSigs == null || storedSigs.length == 0) + && (deviceSigs == null || deviceSigs.length == 0)) { + return true; + } + if (storedSigs == null || deviceSigs == null) { + return false; + } + + // !!! TODO: this demands that every stored signature match one + // that is present on device, and does not demand the converse. + // Is this this right policy? + int nStored = storedSigs.length; + int nDevice = deviceSigs.length; + + for (int i=0; i < nStored; i++) { + boolean match = false; + for (int j=0; j < nDevice; j++) { + if (storedSigs[i].equals(deviceSigs[j])) { + match = true; + break; + } + } + if (!match) { + return false; + } + } + return true; + } + class PerformRestoreThread extends Thread { private IBackupTransport mTransport; private int mToken; @@ -686,6 +801,7 @@ class BackupManagerService extends IBackupManager.Stub { @Override public void run() { + if (DEBUG) Log.v(TAG, "Beginning restore process"); /** * Restore sequence: * @@ -718,6 +834,13 @@ class BackupManagerService extends IBackupManager.Stub { // !!! TODO: pick out the set for this token mImage = images[0]; + // Pull the Package Manager metadata from the restore set first + PackageManagerBackupAgent pmAgent = new PackageManagerBackupAgent( + mPackageManager, allAgentApps()); + PackageInfo pmApp = new PackageInfo(); + pmApp.packageName = PACKAGE_MANAGER_SENTINEL; + processOneRestore(pmApp, IBackupAgent.Stub.asInterface(pmAgent.onBind())); + // build the set of apps we will attempt to restore PackageInfo[] packages = mTransport.getAppSet(mImage.token); HashSet<PackageInfo> appsToRestore = new HashSet<PackageInfo>(); @@ -725,7 +848,21 @@ class BackupManagerService extends IBackupManager.Stub { // get the real PackageManager idea of the package PackageInfo app = isRestorable(pkg); if (app != null) { - appsToRestore.add(app); + // Validate against the backed-up signature block, too + Metadata info = pmAgent.getRestoredMetadata(app.packageName); + if (app.versionCode >= info.versionCode) { + if (DEBUG) Log.v(TAG, "Restore version " + info.versionCode + + " compatible with app version " + app.versionCode); + if (signaturesMatch(info.signatures, app.signatures)) { + appsToRestore.add(app); + } else { + Log.w(TAG, "Sig mismatch restoring " + app.packageName); + } + } else { + Log.i(TAG, "Restore set for " + app.packageName + + " is too new [" + info.versionCode + + "] for installed app version " + app.versionCode); + } } } @@ -849,8 +986,26 @@ class BackupManagerService extends IBackupManager.Stub { // a backup pass for each of them. Log.d(TAG, "dataChanged packageName=" + packageName); - - HashSet<ApplicationInfo> targets = mBackupParticipants.get(Binder.getCallingUid()); + + // If the caller does not hold the BACKUP permission, it can only request a + // backup of its own data. + HashSet<ApplicationInfo> targets; + if ((mContext.checkPermission("android.permission.BACKUP", Binder.getCallingPid(), + Binder.getCallingUid())) == PackageManager.PERMISSION_DENIED) { + targets = mBackupParticipants.get(Binder.getCallingUid()); + } else { + // a caller with full permission can ask to back up any participating app + // !!! TODO: allow backup of ANY app? + if (DEBUG) Log.v(TAG, "Privileged caller, allowing backup of other apps"); + targets = new HashSet<ApplicationInfo>(); + int N = mBackupParticipants.size(); + for (int i = 0; i < N; i++) { + HashSet<ApplicationInfo> s = mBackupParticipants.valueAt(i); + if (s != null) { + targets.addAll(s); + } + } + } if (targets != null) { synchronized (mQueueLock) { // Note that this client has made data changes that need to be backed up @@ -878,8 +1033,7 @@ class BackupManagerService extends IBackupManager.Stub { // Schedule a backup pass in a few minutes. As backup-eligible data // keeps changing, continue to defer the backup pass until things // settle down, to avoid extra overhead. - mBackupHandler.removeMessages(MSG_RUN_BACKUP); - mBackupHandler.sendEmptyMessageDelayed(MSG_RUN_BACKUP, COLLECTION_INTERVAL); + scheduleBackupPassLocked(COLLECTION_INTERVAL); } } else { Log.w(TAG, "dataChanged but no participant pkg " + packageName); @@ -905,14 +1059,14 @@ class BackupManagerService extends IBackupManager.Stub { if (DEBUG) Log.v(TAG, "Scheduling immediate backup pass"); synchronized (mQueueLock) { - mBackupHandler.removeMessages(MSG_RUN_BACKUP); - mBackupHandler.sendEmptyMessage(MSG_RUN_BACKUP); + scheduleBackupPassLocked(0); } } // Report the currently active transport public int getCurrentTransport() { mContext.enforceCallingPermission("android.permission.BACKUP", "selectBackupTransport"); + Log.v(TAG, "getCurrentTransport() returning " + mTransportId); return mTransportId; } @@ -922,6 +1076,7 @@ class BackupManagerService extends IBackupManager.Stub { int prevTransport = mTransportId; mTransportId = transportId; + Log.v(TAG, "selectBackupTransport() set " + mTransportId + " returning " + prevTransport); return prevTransport; } @@ -962,17 +1117,27 @@ class BackupManagerService extends IBackupManager.Stub { // Hand off a restore session public IRestoreSession beginRestoreSession(int transportID) { mContext.enforceCallingPermission("android.permission.BACKUP", "beginRestoreSession"); - return null; + + synchronized(this) { + if (mActiveRestoreSession != null) { + Log.d(TAG, "Restore session requested but one already active"); + return null; + } + mActiveRestoreSession = new RestoreSession(transportID); + } + return mActiveRestoreSession; } // ----- Restore session ----- class RestoreSession extends IRestoreSession.Stub { + private static final String TAG = "RestoreSession"; + private IBackupTransport mRestoreTransport = null; RestoreSet[] mRestoreSets = null; RestoreSession(int transportID) { - mRestoreTransport = createTransport(transportID); + mRestoreTransport = getTransport(transportID); } // --- Binder interface --- @@ -980,12 +1145,18 @@ class BackupManagerService extends IBackupManager.Stub { mContext.enforceCallingPermission("android.permission.BACKUP", "getAvailableRestoreSets"); + try { synchronized(this) { if (mRestoreSets == null) { mRestoreSets = mRestoreTransport.getAvailableRestoreSets(); } return mRestoreSets; } + } catch (RuntimeException e) { + Log.d(TAG, "getAvailableRestoreSets exception"); + e.printStackTrace(); + throw e; + } } public int performRestore(int token) throws android.os.RemoteException { @@ -1001,6 +1172,8 @@ class BackupManagerService extends IBackupManager.Stub { return 0; } } + } else { + if (DEBUG) Log.v(TAG, "No current restore set, not doing restore"); } return -1; } @@ -1011,6 +1184,13 @@ class BackupManagerService extends IBackupManager.Stub { mRestoreTransport.endSession(); mRestoreTransport = null; + synchronized(BackupManagerService.this) { + if (BackupManagerService.this.mActiveRestoreSession == this) { + BackupManagerService.this.mActiveRestoreSession = null; + } else { + Log.e(TAG, "ending non-current restore session"); + } + } } } @@ -1030,11 +1210,10 @@ class BackupManagerService extends IBackupManager.Stub { pw.println(app.toString()); } } - pw.println("Pending:"); - Iterator<BackupRequest> br = mPendingBackups.values().iterator(); - while (br.hasNext()) { - pw.print(" "); - pw.println(br); + pw.println("Pending: " + mPendingBackups.size()); + for (BackupRequest req : mPendingBackups.values()) { + pw.print(" "); + pw.println(req); } } } diff --git a/services/java/com/android/server/EntropyService.java b/services/java/com/android/server/EntropyService.java new file mode 100644 index 0000000..e51a0af --- /dev/null +++ b/services/java/com/android/server/EntropyService.java @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server; + +import java.io.File; +import java.io.IOException; + +import android.os.Binder; +import android.os.Environment; +import android.os.Handler; +import android.os.Message; +import android.util.Log; + +/** + * A service designed to load and periodically save "randomness" + * for the Linux kernel. + * + * <p>When a Linux system starts up, the entropy pool associated with + * {@code /dev/random} may be in a fairly predictable state. Applications which + * depend strongly on randomness may find {@code /dev/random} or + * {@code /dev/urandom} returning predictable data. In order to counteract + * this effect, it's helpful to carry the entropy pool information across + * shutdowns and startups. + * + * <p>This class was modeled after the script in + * <a href="http://www.kernel.org/doc/man-pages/online/pages/man4/random.4.html">man + * 4 random</a>. + * + * <p>TODO: Investigate attempting to write entropy data at shutdown time + * instead of periodically. + */ +public class EntropyService extends Binder { + private static final String ENTROPY_FILENAME = getSystemDir() + "/entropy.dat"; + private static final String TAG = "EntropyService"; + private static final int ENTROPY_WHAT = 1; + private static final int ENTROPY_WRITE_PERIOD = 3 * 60 * 60 * 1000; // 3 hrs + private static final String RANDOM_DEV = "/dev/urandom"; + + /** + * Handler that periodically updates the entropy on disk. + */ + private final Handler mHandler = new Handler() { + @Override + public void handleMessage(Message msg) { + if (msg.what != ENTROPY_WHAT) { + Log.e(TAG, "Will not process invalid message"); + return; + } + writeEntropy(); + scheduleEntropyWriter(); + } + }; + + public EntropyService() { + loadInitialEntropy(); + writeEntropy(); + scheduleEntropyWriter(); + } + + private void scheduleEntropyWriter() { + mHandler.removeMessages(ENTROPY_WHAT); + mHandler.sendEmptyMessageDelayed(ENTROPY_WHAT, ENTROPY_WRITE_PERIOD); + } + + private void loadInitialEntropy() { + try { + RandomBlock.fromFile(ENTROPY_FILENAME).toFile(RANDOM_DEV); + } catch (IOException e) { + Log.w(TAG, "unable to load initial entropy (first boot?)", e); + } + } + + private void writeEntropy() { + try { + RandomBlock.fromFile(RANDOM_DEV).toFile(ENTROPY_FILENAME); + } catch (IOException e) { + Log.e(TAG, "unable to write entropy", e); + } + } + + private static String getSystemDir() { + File dataDir = Environment.getDataDirectory(); + File systemDir = new File(dataDir, "system"); + systemDir.mkdirs(); + return systemDir.toString(); + } +} diff --git a/services/java/com/android/server/PackageManagerBackupAgent.java b/services/java/com/android/server/PackageManagerBackupAgent.java new file mode 100644 index 0000000..cc84430 --- /dev/null +++ b/services/java/com/android/server/PackageManagerBackupAgent.java @@ -0,0 +1,292 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server; + +import android.app.BackupAgent; +import android.backup.BackupDataInput; +import android.backup.BackupDataOutput; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.content.pm.PackageManager.NameNotFoundException; +import android.content.pm.Signature; +import android.os.ParcelFileDescriptor; +import android.util.Log; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.EOFException; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; + +/** + * We back up the signatures of each package so that during a system restore, + * we can verify that the app whose data we think we have matches the app + * actually resident on the device. + * + * Since the Package Manager isn't a proper "application" we just provide a + * direct IBackupAgent implementation and hand-construct it at need. + */ +public class PackageManagerBackupAgent extends BackupAgent { + private static final String TAG = "PMBA"; + private static final boolean DEBUG = true; + + private List<ApplicationInfo> mAllApps; + private PackageManager mPackageManager; + private HashMap<String, Metadata> mRestoredSignatures; + + public class Metadata { + public int versionCode; + public Signature[] signatures; + + Metadata(int version, Signature[] sigs) { + versionCode = version; + signatures = sigs; + } + } + + // We're constructed with the set of applications that are participating + // in backup. This set changes as apps are installed & removed. + PackageManagerBackupAgent(PackageManager packageMgr, List<ApplicationInfo> apps) { + mPackageManager = packageMgr; + mAllApps = apps; + mRestoredSignatures = null; + } + + public Metadata getRestoredMetadata(String packageName) { + if (mRestoredSignatures == null) { + return null; + } + + return mRestoredSignatures.get(packageName); + } + + // The backed up data is the signature block for each app, keyed by + // the package name. + public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data, + ParcelFileDescriptor newState) { + HashSet<String> existing = parseStateFile(oldState); + + // For each app we have on device, see if we've backed it up yet. If not, + // write its signature block to the output, keyed on the package name. + if (DEBUG) Log.v(TAG, "onBackup()"); + ByteArrayOutputStream bufStream = new ByteArrayOutputStream(); // we'll reuse these + DataOutputStream outWriter = new DataOutputStream(bufStream); + for (ApplicationInfo app : mAllApps) { + String packName = app.packageName; + if (!existing.contains(packName)) { + // We haven't stored this app's signatures yet, so we do that now + try { + PackageInfo info = mPackageManager.getPackageInfo(packName, + PackageManager.GET_SIGNATURES); + /* + * Metadata for each package: + * + * int version -- [4] the package's versionCode + * byte[] signatures -- [len] flattened Signature[] of the package + */ + + // marshall the version code in a canonical form + bufStream.reset(); + outWriter.writeInt(info.versionCode); + byte[] versionBuf = bufStream.toByteArray(); + + byte[] sigs = flattenSignatureArray(info.signatures); + + // !!! TODO: take out this debugging + if (DEBUG) { + Log.v(TAG, "+ metadata for " + packName + " version=" + info.versionCode); + } + // Now we can write the backup entity for this package + data.writeEntityHeader(packName, versionBuf.length + sigs.length); + data.writeEntityData(versionBuf, versionBuf.length); + data.writeEntityData(sigs, sigs.length); + } catch (NameNotFoundException e) { + // Weird; we just found it, and now are told it doesn't exist. + // Treat it as having been removed from the device. + existing.add(packName); + } catch (IOException e) { + // Real error writing data + Log.e(TAG, "Unable to write package backup data file!"); + return; + } + } else { + // We've already backed up this app. Remove it from the set so + // we can tell at the end what has disappeared from the device. + // !!! TODO: take out the debugging message + if (DEBUG) Log.v(TAG, "= already backed up metadata for " + packName); + if (!existing.remove(packName)) { + Log.d(TAG, "*** failed to remove " + packName + " from package set!"); + } + } + } + + // At this point, the only entries in 'existing' are apps that were + // mentioned in the saved state file, but appear to no longer be present + // on the device. Write a deletion entity for them. + for (String app : existing) { + // !!! TODO: take out this msg + if (DEBUG) Log.v(TAG, "- removing metadata for deleted pkg " + app); + try { + data.writeEntityHeader(app, -1); + } catch (IOException e) { + Log.e(TAG, "Unable to write package deletions!"); + return; + } + } + + // Finally, write the new state blob -- just the list of all apps we handled + writeStateFile(mAllApps, newState); + } + + // "Restore" here is a misnomer. What we're really doing is reading back the + // set of app signatures associated with each backed-up app in this restore + // image. We'll use those later to determine what we can legitimately restore. + public void onRestore(BackupDataInput data, ParcelFileDescriptor newState) + throws IOException { + List<ApplicationInfo> restoredApps = new ArrayList<ApplicationInfo>(); + HashMap<String, Metadata> sigMap = new HashMap<String, Metadata>(); + + while (data.readNextHeader()) { + int dataSize = data.getDataSize(); + byte[] buf = new byte[dataSize]; + data.readEntityData(buf, 0, dataSize); + + ByteArrayInputStream bufStream = new ByteArrayInputStream(buf); + DataInputStream in = new DataInputStream(bufStream); + int versionCode = in.readInt(); + + Signature[] sigs = unflattenSignatureArray(in); + String pkg = data.getKey(); +// !!! TODO: take out this debugging + if (DEBUG) { + Log.i(TAG, "+ restored metadata for " + pkg + + " versionCode=" + versionCode + " sigs=" + sigs); + } + + ApplicationInfo app = new ApplicationInfo(); + app.packageName = pkg; + restoredApps.add(app); + sigMap.put(pkg, new Metadata(versionCode, sigs)); + } + + mRestoredSignatures = sigMap; + } + + + // Util: convert an array of Signatures into a flattened byte buffer. The + // flattened format contains enough info to reconstruct the signature array. + private byte[] flattenSignatureArray(Signature[] allSigs) { + ByteArrayOutputStream outBuf = new ByteArrayOutputStream(); + DataOutputStream out = new DataOutputStream(outBuf); + + // build the set of subsidiary buffers + try { + // first the # of signatures in the array + out.writeInt(allSigs.length); + + // then the signatures themselves, length + flattened buffer + for (Signature sig : allSigs) { + byte[] flat = sig.toByteArray(); + out.writeInt(flat.length); + out.write(flat); + } + } catch (IOException e) { + // very strange; we're writing to memory here. abort. + return null; + } + + return outBuf.toByteArray(); + } + + private Signature[] unflattenSignatureArray(/*byte[] buffer*/ DataInputStream in) { + Signature[] sigs = null; + + try { + int num = in.readInt(); + sigs = new Signature[num]; + for (int i = 0; i < num; i++) { + int len = in.readInt(); + byte[] flatSig = new byte[len]; + in.read(flatSig); + sigs[i] = new Signature(flatSig); + } + } catch (EOFException e) { + // clean termination + if (sigs == null) { + Log.w(TAG, "Empty signature block found"); + } + } catch (IOException e) { + Log.d(TAG, "Unable to unflatten sigs"); + return null; + } + + return sigs; + } + + // Util: parse out an existing state file into a usable structure + private HashSet<String> parseStateFile(ParcelFileDescriptor stateFile) { + HashSet<String> set = new HashSet<String>(); + // The state file is just the list of app names we have stored signatures for + FileInputStream instream = new FileInputStream(stateFile.getFileDescriptor()); + DataInputStream in = new DataInputStream(instream); + + int bufSize = 256; + byte[] buf = new byte[bufSize]; + try { + int nameSize = in.readInt(); + if (bufSize < nameSize) { + bufSize = nameSize + 32; + buf = new byte[bufSize]; + } + in.read(buf, 0, nameSize); + String pkg = new String(buf, 0, nameSize); + set.add(pkg); + } catch (EOFException eof) { + // safe; we're done + } catch (IOException e) { + // whoops, bad state file. abort. + Log.e(TAG, "Unable to read Package Manager state file"); + return null; + } + return set; + } + + // Util: write a set of names into a new state file + private void writeStateFile(List<ApplicationInfo> apps, ParcelFileDescriptor stateFile) { + FileOutputStream outstream = new FileOutputStream(stateFile.getFileDescriptor()); + DataOutputStream out = new DataOutputStream(outstream); + + for (ApplicationInfo app : apps) { + try { + byte[] pkgNameBuf = app.packageName.getBytes(); + out.writeInt(pkgNameBuf.length); + out.write(pkgNameBuf); + } catch (IOException e) { + Log.e(TAG, "Unable to write package manager state file!"); + return; + } + } + } +} diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java index d98738a..4b583e4 100644 --- a/services/java/com/android/server/PackageManagerService.java +++ b/services/java/com/android/server/PackageManagerService.java @@ -19,7 +19,6 @@ package com.android.server; import com.android.internal.app.ResolverActivity; import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.XmlUtils; -import com.android.server.PackageManagerService.PreferredActivity; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; @@ -27,13 +26,12 @@ import org.xmlpull.v1.XmlSerializer; import android.app.ActivityManagerNative; import android.app.IActivityManager; -import android.app.PendingIntent; -import android.app.PendingIntent.CanceledException; import android.content.ComponentName; -import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.IntentSender; +import android.content.IntentSender.SendIntentException; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.ComponentInfo; @@ -150,6 +148,7 @@ class PackageManagerService extends IPackageManager.Stub { final Context mContext; final boolean mFactoryTest; + final boolean mNoDexOpt; final DisplayMetrics mMetrics; final int mDefParseFlags; final String[] mSeparateProcesses; @@ -301,6 +300,7 @@ class PackageManagerService extends IPackageManager.Stub { mContext = context; mFactoryTest = factoryTest; + mNoDexOpt = "eng".equals(SystemProperties.get("ro.build.type")); mMetrics = new DisplayMetrics(); mSettings = new Settings(); mSettings.addSharedUserLP("android.uid.system", @@ -370,6 +370,10 @@ class PackageManagerService extends IPackageManager.Stub { startTime); int scanMode = SCAN_MONITOR; + if (mNoDexOpt) { + Log.w(TAG, "Running ENG build: no pre-dexopt!"); + scanMode |= SCAN_NO_DEX; + } final HashSet<String> libFiles = new HashSet<String>(); @@ -930,7 +934,7 @@ class PackageManagerService extends IPackageManager.Stub { }); } - public void freeStorage(final long freeStorageSize, final PendingIntent opFinishedIntent) { + public void freeStorage(final long freeStorageSize, final IntentSender pi) { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.CLEAR_APP_CACHE, null); // Queue up an async operation since clearing cache may take a little while. @@ -944,11 +948,13 @@ class PackageManagerService extends IPackageManager.Stub { Log.w(TAG, "Couldn't clear application caches"); } } - if(opFinishedIntent != null) { + if(pi != null) { try { // Callback via pending intent - opFinishedIntent.send((retCode >= 0) ? 1 : 0); - } catch (CanceledException e1) { + int code = (retCode >= 0) ? 1 : 0; + pi.sendIntent(null, code, null, + null, null); + } catch (SendIntentException e1) { Log.i(TAG, "Failed to send pending intent"); } } @@ -963,10 +969,11 @@ class PackageManagerService extends IPackageManager.Stub { if (Config.LOGV) Log.v(TAG, "getActivityInfo " + component + ": " + a); if (a != null && mSettings.isEnabledLP(a.info, flags)) { ActivityInfo ainfo = PackageParser.generateActivityInfo(a, flags); - if (ainfo != null && (flags & PackageManager.GET_EXPANDABLE) != 0) { + if (ainfo != null) { ApplicationInfo appInfo = getApplicationInfo(component.getPackageName(), - PackageManager.GET_EXPANDABLE | PackageManager.GET_SUPPORTS_DENSITIES); - if (appInfo != null && !appInfo.expandable) { + PackageManager.GET_SUPPORTS_DENSITIES); + if (appInfo != null && + (appInfo.flags & ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS) == 0) { // Check if the screen size is same as what the application expect. CompatibilityInfo info = new CompatibilityInfo(appInfo); DisplayMetrics metrics = new DisplayMetrics(); @@ -979,11 +986,13 @@ class PackageManagerService extends IPackageManager.Stub { // Don't allow an app that cannot expand to handle rotation. ainfo.configChanges &= ~ ActivityInfo.CONFIG_ORIENTATION; } else { - appInfo.expandable = true; + appInfo.flags |= ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS; } if (DEBUG_SETTINGS) { Log.d(TAG, "component=" + component + - ", expandable:" + appInfo.expandable); + ", expandable:" + + ((appInfo.flags & + ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS) != 0)); } } } @@ -1239,28 +1248,6 @@ class PackageManagerService extends IPackageManager.Stub { return chooseBestActivity(intent, resolvedType, flags, query); } - public ResolveInfo resolveIntentForPackage(Intent intent, String resolvedType, - int flags, String packageName) { - ComponentName comp = intent.getComponent(); - if (comp != null) { - // if this is an explicit intent, it must have the same the packageName - if (packageName.equals(comp.getPackageName())) { - return resolveIntent(intent, resolvedType, flags); - } - return null; - } else { - List<ResolveInfo> query = null; - synchronized (mPackages) { - PackageParser.Package pkg = mPackages.get(packageName); - if (pkg != null) { - query = (List<ResolveInfo>) mActivities. - queryIntentForPackage(intent, resolvedType, flags, pkg.activities); - } - } - return chooseBestActivity(intent, resolvedType, flags, query); - } - } - private ResolveInfo chooseBestActivity(Intent intent, String resolvedType, int flags, List<ResolveInfo> query) { if (query != null) { @@ -1379,8 +1366,17 @@ class PackageManagerService extends IPackageManager.Stub { } synchronized (mPackages) { - return (List<ResolveInfo>)mActivities. - queryIntent(intent, resolvedType, flags); + String pkgName = intent.getPackage(); + if (pkgName == null) { + return (List<ResolveInfo>)mActivities.queryIntent(intent, + resolvedType, flags); + } + PackageParser.Package pkg = mPackages.get(pkgName); + if (pkg != null) { + return (List<ResolveInfo>) mActivities.queryIntentForPackage(intent, + resolvedType, flags, pkg.activities); + } + return null; } } @@ -1547,9 +1543,30 @@ class PackageManagerService extends IPackageManager.Stub { public List<ResolveInfo> queryIntentReceivers(Intent intent, String resolvedType, int flags) { + ComponentName comp = intent.getComponent(); + if (comp != null) { + List<ResolveInfo> list = new ArrayList<ResolveInfo>(1); + ActivityInfo ai = getReceiverInfo(comp, flags); + if (ai != null) { + ResolveInfo ri = new ResolveInfo(); + ri.activityInfo = ai; + list.add(ri); + } + return list; + } + synchronized (mPackages) { - return (List<ResolveInfo>)mReceivers. - queryIntent(intent, resolvedType, flags); + String pkgName = intent.getPackage(); + if (pkgName == null) { + return (List<ResolveInfo>)mReceivers.queryIntent(intent, + resolvedType, flags); + } + PackageParser.Package pkg = mPackages.get(pkgName); + if (pkg != null) { + return (List<ResolveInfo>) mReceivers.queryIntentForPackage(intent, + resolvedType, flags, pkg.receivers); + } + return null; } } @@ -1582,7 +1599,17 @@ class PackageManagerService extends IPackageManager.Stub { } synchronized (mPackages) { - return (List<ResolveInfo>)mServices.queryIntent(intent, resolvedType, flags); + String pkgName = intent.getPackage(); + if (pkgName == null) { + return (List<ResolveInfo>)mServices.queryIntent(intent, + resolvedType, flags); + } + PackageParser.Package pkg = mPackages.get(pkgName); + if (pkg != null) { + return (List<ResolveInfo>)mServices.queryIntentForPackage(intent, + resolvedType, flags, pkg.services); + } + return null; } } @@ -1898,7 +1925,56 @@ class PackageManagerService extends IPackageManager.Stub { } return true; } + + public boolean performDexOpt(String packageName) { + if (!mNoDexOpt) { + return false; + } + PackageParser.Package p; + synchronized (mPackages) { + p = mPackages.get(packageName); + if (p == null || p.mDidDexOpt) { + return false; + } + } + synchronized (mInstallLock) { + return performDexOptLI(p, false) == DEX_OPT_PERFORMED; + } + } + + static final int DEX_OPT_SKIPPED = 0; + static final int DEX_OPT_PERFORMED = 1; + static final int DEX_OPT_FAILED = -1; + + private int performDexOptLI(PackageParser.Package pkg, boolean forceDex) { + boolean performed = false; + if ((pkg.applicationInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0) { + String path = pkg.mScanPath; + int ret = 0; + try { + if (forceDex || dalvik.system.DexFile.isDexOptNeeded(path)) { + ret = mInstaller.dexopt(path, pkg.applicationInfo.uid, + !pkg.mForwardLocked); + pkg.mDidDexOpt = true; + performed = true; + } + } catch (FileNotFoundException e) { + Log.w(TAG, "Apk not found for dexopt: " + path); + ret = -1; + } catch (IOException e) { + Log.w(TAG, "Exception reading apk: " + path, e); + ret = -1; + } + if (ret < 0) { + //error from installer + return DEX_OPT_FAILED; + } + } + + return performed ? DEX_OPT_PERFORMED : DEX_OPT_SKIPPED; + } + private PackageParser.Package scanPackageLI( File scanFile, File destCodeFile, File destResourceFile, PackageParser.Package pkg, int parseFlags, int scanMode) { @@ -2194,23 +2270,11 @@ class PackageManagerService extends IPackageManager.Stub { } } - if ((scanMode&SCAN_NO_DEX) == 0 - && (pkg.applicationInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0) { - int ret = 0; - try { - if (forceDex || dalvik.system.DexFile.isDexOptNeeded(path)) { - ret = mInstaller.dexopt(path, pkg.applicationInfo.uid, - (scanMode&SCAN_FORWARD_LOCKED) == 0); - } - } catch (FileNotFoundException e) { - Log.w(TAG, "Apk not found for dexopt: " + path); - ret = -1; - } catch (IOException e) { - Log.w(TAG, "Exception reading apk: " + path, e); - ret = -1; - } - if (ret < 0) { - //error from installer + pkg.mForwardLocked = (scanMode&SCAN_FORWARD_LOCKED) != 0; + pkg.mScanPath = path; + + if ((scanMode&SCAN_NO_DEX) == 0) { + if (performDexOptLI(pkg, forceDex) == DEX_OPT_FAILED) { mLastScanError = PackageManager.INSTALL_FAILED_DEXOPT; return null; } @@ -3079,6 +3143,27 @@ class PackageManagerService extends IPackageManager.Stub { (flags&PackageManager.MATCH_DEFAULT_ONLY) != 0); } + public List queryIntentForPackage(Intent intent, String resolvedType, int flags, + ArrayList<PackageParser.Service> packageServices) { + if (packageServices == null) { + return null; + } + mFlags = flags; + final boolean defaultOnly = (flags&PackageManager.MATCH_DEFAULT_ONLY) != 0; + int N = packageServices.size(); + ArrayList<ArrayList<PackageParser.ServiceIntentInfo>> listCut = + new ArrayList<ArrayList<PackageParser.ServiceIntentInfo>>(N); + + ArrayList<PackageParser.ServiceIntentInfo> intentFilters; + for (int i = 0; i < N; ++i) { + intentFilters = packageServices.get(i).intents; + if (intentFilters != null && intentFilters.size() > 0) { + listCut.add(intentFilters); + } + } + return super.queryIntentFromList(intent, resolvedType, defaultOnly, listCut); + } + public final void addService(PackageParser.Service s) { mServices.put(s.component, s); if (SHOW_INFO || Config.LOGV) Log.v( diff --git a/services/java/com/android/server/RandomBlock.java b/services/java/com/android/server/RandomBlock.java new file mode 100644 index 0000000..4ac1c6e --- /dev/null +++ b/services/java/com/android/server/RandomBlock.java @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server; + +import android.util.Log; + +import java.io.Closeable; +import java.io.DataOutput; +import java.io.EOFException; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.RandomAccessFile; + +/** + * A 4k block of random {@code byte}s. + */ +class RandomBlock { + + private static final String TAG = "RandomBlock"; + private static final int BLOCK_SIZE = 4096; + private byte[] block = new byte[BLOCK_SIZE]; + + private RandomBlock() { } + + static RandomBlock fromFile(String filename) throws IOException { + Log.v(TAG, "reading from file " + filename); + InputStream stream = null; + try { + stream = new FileInputStream(filename); + return fromStream(stream); + } finally { + close(stream); + } + } + + private static RandomBlock fromStream(InputStream in) throws IOException { + RandomBlock retval = new RandomBlock(); + int total = 0; + while(total < BLOCK_SIZE) { + int result = in.read(retval.block, total, BLOCK_SIZE - total); + if (result == -1) { + throw new EOFException(); + } + total += result; + } + return retval; + } + + void toFile(String filename) throws IOException { + Log.v(TAG, "writing to file " + filename); + RandomAccessFile out = null; + try { + out = new RandomAccessFile(filename, "rws"); + toDataOut(out); + truncateIfPossible(out); + } finally { + close(out); + } + } + + private static void truncateIfPossible(RandomAccessFile f) { + try { + f.setLength(BLOCK_SIZE); + } catch (IOException e) { + // ignore this exception. Sometimes, the file we're trying to + // write is a character device, such as /dev/urandom, and + // these character devices do not support setting the length. + } + } + + private void toDataOut(DataOutput out) throws IOException { + out.write(block); + } + + private static void close(Closeable c) { + try { + if (c == null) { + return; + } + c.close(); + } catch (IOException e) { + Log.w(TAG, "IOException thrown while closing Closeable", e); + } + } +} diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 36fc5bb..c26ba5c 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -93,6 +93,9 @@ class ServerThread extends Thread { // Critical services... try { + Log.i(TAG, "Starting Entropy Service."); + ServiceManager.addService("entropy", new EntropyService()); + Log.i(TAG, "Starting Power Manager."); power = new PowerManagerService(); ServiceManager.addService(Context.POWER_SERVICE, power); diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index 07d6b6f..ac176ca 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -34,8 +34,6 @@ import android.app.Dialog; import android.app.IActivityWatcher; import android.app.IApplicationThread; import android.app.IInstrumentationWatcher; -import android.app.IIntentReceiver; -import android.app.IIntentSender; import android.app.IServiceConnection; import android.app.IThumbnailReceiver; import android.app.Instrumentation; @@ -48,6 +46,8 @@ import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.IIntentReceiver; +import android.content.IIntentSender; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.ConfigurationInfo; @@ -61,7 +61,6 @@ import android.content.pm.ServiceInfo; import android.content.res.Configuration; import android.graphics.Bitmap; import android.net.Uri; -import android.os.BatteryStats; import android.os.Binder; import android.os.Bundle; import android.os.Environment; @@ -88,7 +87,6 @@ import android.text.TextUtils; import android.util.Config; import android.util.EventLog; import android.util.Log; -import android.util.LogPrinter; import android.util.PrintWriterPrinter; import android.util.SparseArray; import android.view.Gravity; @@ -127,6 +125,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen static final boolean DEBUG_OOM_ADJ = localLOGV || false; static final boolean DEBUG_TRANSITION = localLOGV || false; static final boolean DEBUG_BROADCAST = localLOGV || false; + static final boolean DEBUG_BROADCAST_LIGHT = DEBUG_BROADCAST || false; static final boolean DEBUG_SERVICE = localLOGV || false; static final boolean DEBUG_VISBILITY = localLOGV || false; static final boolean DEBUG_PROCESSES = localLOGV || false; @@ -182,7 +181,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // The flags that are set for all calls we make to the package manager. static final int STOCK_PM_FLAGS = PackageManager.GET_SHARED_LIBRARY_FILES - | PackageManager.GET_SUPPORTS_DENSITIES | PackageManager.GET_EXPANDABLE; + | PackageManager.GET_SUPPORTS_DENSITIES; private static final String SYSTEM_SECURE = "ro.secure"; @@ -316,6 +315,12 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // Memory pages are 4K. static final int PAGE_SIZE = 4*1024; + // System property defining error report receiver for system apps + static final String SYSTEM_APPS_ERROR_RECEIVER_PROPERTY = "ro.error.receiver.system.apps"; + + // System property defining default error report receiver + static final String DEFAULT_ERROR_RECEIVER_PROPERTY = "ro.error.receiver.default"; + // Corresponding memory levels for above adjustments. final int EMPTY_APP_MEM; final int HIDDEN_APP_MEM; @@ -810,6 +815,12 @@ public final class ActivityManagerService extends ActivityManagerNative implemen */ int[] mProcDeaths = new int[20]; + /** + * This is set if we had to do a delayed dexopt of an app before launching + * it, to increasing the ANR timeouts in that case. + */ + boolean mDidDexOpt; + String mDebugApp = null; boolean mWaitForDebugger = false; boolean mDebugTransient = false; @@ -1008,6 +1019,12 @@ public final class ActivityManagerService extends ActivityManagerNative implemen processNextBroadcast(true); } break; case BROADCAST_TIMEOUT_MSG: { + if (mDidDexOpt) { + mDidDexOpt = false; + Message nmsg = mHandler.obtainMessage(BROADCAST_TIMEOUT_MSG); + mHandler.sendMessageDelayed(nmsg, BROADCAST_TIMEOUT); + return; + } broadcastTimeout(); } break; case PAUSE_TIMEOUT_MSG: { @@ -1018,9 +1035,16 @@ public final class ActivityManagerService extends ActivityManagerNative implemen activityPaused(token, null, true); } break; case IDLE_TIMEOUT_MSG: { - IBinder token = (IBinder)msg.obj; + if (mDidDexOpt) { + mDidDexOpt = false; + Message nmsg = mHandler.obtainMessage(IDLE_TIMEOUT_MSG); + nmsg.obj = msg.obj; + mHandler.sendMessageDelayed(nmsg, IDLE_TIMEOUT); + return; + } // We don't at this point know if the activity is fullscreen, // so we need to be conservative and assume it isn't. + IBinder token = (IBinder)msg.obj; Log.w(TAG, "Activity idle timeout for " + token); activityIdleInternal(token, true); } break; @@ -1036,6 +1060,13 @@ public final class ActivityManagerService extends ActivityManagerNative implemen activityIdle(token); } break; case SERVICE_TIMEOUT_MSG: { + if (mDidDexOpt) { + mDidDexOpt = false; + Message nmsg = mHandler.obtainMessage(SERVICE_TIMEOUT_MSG); + nmsg.obj = msg.obj; + mHandler.sendMessageDelayed(nmsg, SERVICE_TIMEOUT); + return; + } serviceTimeout((ProcessRecord)msg.obj); } break; case UPDATE_TIME_ZONE: { @@ -1072,6 +1103,12 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } } break; case LAUNCH_TIMEOUT_MSG: { + if (mDidDexOpt) { + mDidDexOpt = false; + Message nmsg = mHandler.obtainMessage(LAUNCH_TIMEOUT_MSG); + mHandler.sendMessageDelayed(nmsg, LAUNCH_TIMEOUT); + return; + } synchronized (ActivityManagerService.this) { if (mLaunchingActivity.isHeld()) { Log.w(TAG, "Launch timeout has expired, giving up wake lock!"); @@ -1092,6 +1129,13 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } } case PROC_START_TIMEOUT_MSG: { + if (mDidDexOpt) { + mDidDexOpt = false; + Message nmsg = mHandler.obtainMessage(PROC_START_TIMEOUT_MSG); + nmsg.obj = msg.obj; + mHandler.sendMessageDelayed(nmsg, PROC_START_TIMEOUT); + return; + } ProcessRecord app = (ProcessRecord)msg.obj; synchronized (ActivityManagerService.this) { processStartTimedOutLocked(app); @@ -1608,6 +1652,16 @@ public final class ActivityManagerService extends ActivityManagerNative implemen return proc; } + private void ensurePackageDexOpt(String packageName) { + IPackageManager pm = ActivityThread.getPackageManager(); + try { + if (pm.performDexOpt(packageName)) { + mDidDexOpt = true; + } + } catch (RemoteException e) { + } + } + private boolean isNextTransitionForward() { int transit = mWindowManager.getPendingAppTransition(); return transit == WindowManagerPolicy.TRANSIT_ACTIVITY_OPEN @@ -1667,6 +1721,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen if (r.isHomeActivity) { mHomeProcess = app; } + ensurePackageDexOpt(r.intent.getComponent().getPackageName()); app.thread.scheduleLaunchActivity(new Intent(r.intent), r, r.info, r.icicle, results, newIntents, !andResume, isNextTransitionForward()); @@ -4820,6 +4875,10 @@ public final class ActivityManagerService extends ActivityManagerNative implemen isRestrictedBackupMode = (mBackupTarget.backupMode == BackupRecord.RESTORE) || (mBackupTarget.backupMode == BackupRecord.BACKUP_FULL); } + ensurePackageDexOpt(app.info.packageName); + if (app.instrumentationInfo != null) { + ensurePackageDexOpt(app.instrumentationInfo.packageName); + } thread.bindApplication(processName, app.instrumentationInfo != null ? app.instrumentationInfo : app.info, providers, app.instrumentationClass, app.instrumentationProfileFile, @@ -4908,6 +4967,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // Check whether the next backup agent is in this process... if (!badApp && mBackupTarget != null && mBackupTarget.appInfo.uid == app.info.uid) { if (DEBUG_BACKUP) Log.v(TAG, "New app is backup target, launching agent for " + app); + ensurePackageDexOpt(mBackupTarget.appInfo.packageName); try { thread.scheduleCreateBackupAgent(mBackupTarget.appInfo, mBackupTarget.backupMode); } catch (Exception e) { @@ -6919,6 +6979,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } app.pubProviders.put(cpi.name, cpr); app.addPackage(cpi.applicationInfo.packageName); + ensurePackageDexOpt(cpi.applicationInfo.packageName); } } return providers; @@ -7895,26 +7956,62 @@ public final class ActivityManagerService extends ActivityManagerNative implemen private ComponentName getErrorReportReceiver(ProcessRecord app) { IPackageManager pm = ActivityThread.getPackageManager(); + try { - // was an installer package name specified when this app was - // installed? - String installerPackageName = pm.getInstallerPackageName(app.info.packageName); - if (installerPackageName == null) { - return null; + // look for receiver in the installer package + String candidate = pm.getInstallerPackageName(app.info.packageName); + ComponentName result = getErrorReportReceiver(pm, app.info.packageName, candidate); + if (result != null) { + return result; } - // is there an Activity in this package that handles ACTION_APP_ERROR? - Intent intent = new Intent(Intent.ACTION_APP_ERROR); - ResolveInfo info = pm.resolveIntentForPackage(intent, null, 0, installerPackageName); - if (info == null || info.activityInfo == null) { - return null; + // if the error app is on the system image, look for system apps + // error receiver + if ((app.info.flags&ApplicationInfo.FLAG_SYSTEM) != 0) { + candidate = SystemProperties.get(SYSTEM_APPS_ERROR_RECEIVER_PROPERTY); + result = getErrorReportReceiver(pm, app.info.packageName, candidate); + if (result != null) { + return result; + } } - return new ComponentName(installerPackageName, info.activityInfo.name); + // if there is a default receiver, try that + candidate = SystemProperties.get(DEFAULT_ERROR_RECEIVER_PROPERTY); + return getErrorReportReceiver(pm, app.info.packageName, candidate); } catch (RemoteException e) { - // will return null and no error report will be delivered + // should not happen + Log.e(TAG, "error talking to PackageManager", e); + return null; } - return null; + } + + /** + * Return activity in receiverPackage that handles ACTION_APP_ERROR. + * + * @param pm PackageManager isntance + * @param errorPackage package which caused the error + * @param receiverPackage candidate package to receive the error + * @return activity component within receiverPackage which handles + * ACTION_APP_ERROR, or null if not found + */ + private ComponentName getErrorReportReceiver(IPackageManager pm, String errorPackage, + String receiverPackage) throws RemoteException { + if (receiverPackage == null || receiverPackage.length() == 0) { + return null; + } + + // break the loop if it's the error report receiver package that crashed + if (receiverPackage.equals(errorPackage)) { + return null; + } + + Intent intent = new Intent(Intent.ACTION_APP_ERROR); + intent.setPackage(receiverPackage); + ResolveInfo info = pm.resolveIntent(intent, null, 0); + if (info == null || info.activityInfo == null) { + return null; + } + return new ComponentName(receiverPackage, info.activityInfo.name); } void makeAppNotRespondingLocked(ProcessRecord app, @@ -8219,12 +8316,19 @@ public final class ActivityManagerService extends ActivityManagerNative implemen report.time = crashData.getTime(); report.crashInfo.stackTrace = throwData.toString(); - // extract the source of the exception, useful for report - // clustering + // Extract the source of the exception, useful for report + // clustering. Also extract the "deepest" non-null exception + // message. + String exceptionMessage = throwData.getMessage(); while (throwData.getCause() != null) { throwData = throwData.getCause(); + String msg = throwData.getMessage(); + if (msg != null && msg.length() > 0) { + exceptionMessage = msg; + } } StackTraceElementData trace = throwData.getStackTrace()[0]; + report.crashInfo.exceptionMessage = exceptionMessage; report.crashInfo.exceptionClassName = throwData.getType(); report.crashInfo.throwFileName = trace.getFileName(); report.crashInfo.throwClassName = trace.getClassName(); @@ -9535,6 +9639,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen synchronized (r.stats.getBatteryStats()) { r.stats.startLaunchedLocked(); } + ensurePackageDexOpt(r.serviceInfo.packageName); app.thread.scheduleCreateService(r, r.serviceInfo); created = true; } finally { @@ -10596,7 +10701,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen boolean ordered, boolean sticky, int callingPid, int callingUid) { intent = new Intent(intent); - if (DEBUG_BROADCAST) Log.v( + if (DEBUG_BROADCAST_LIGHT) Log.v( TAG, (sticky ? "Broadcast sticky: ": "Broadcast: ") + intent + " ordered=" + ordered); if ((resultTo != null) && !ordered) { @@ -11088,9 +11193,10 @@ public final class ActivityManagerService extends ActivityManagerNative implemen boolean started = false; try { - if (DEBUG_BROADCAST) Log.v(TAG, + if (DEBUG_BROADCAST_LIGHT) Log.v(TAG, "Delivering to component " + r.curComponent + ": " + r); + ensurePackageDexOpt(r.intent.getComponent().getPackageName()); app.thread.scheduleReceiver(new Intent(r.intent), r.curReceiver, r.resultCode, r.resultData, r.resultExtras, r.ordered); started = true; @@ -11158,12 +11264,22 @@ public final class ActivityManagerService extends ActivityManagerNative implemen r.curFilter = filter; filter.receiverList.curBroadcast = r; r.state = BroadcastRecord.CALL_IN_RECEIVE; + if (filter.receiverList.app != null) { + // Bump hosting application to no longer be in background + // scheduling class. Note that we can't do that if there + // isn't an app... but we can only be in that case for + // things that directly call the IActivityManager API, which + // are already core system stuff so don't matter for this. + r.curApp = filter.receiverList.app; + filter.receiverList.app.curReceiver = r; + updateOomAdjLocked(); + } } try { - if (DEBUG_BROADCAST) { + if (DEBUG_BROADCAST_LIGHT) { int seq = r.intent.getIntExtra("seq", -1); - Log.i(TAG, "Sending broadcast " + r.intent.getAction() + " seq=" + seq - + " app=" + filter.receiverList.app); + Log.i(TAG, "Delivering to " + filter.receiverList.app + + " (seq=" + seq + "): " + r); } performReceive(filter.receiverList.app, filter.receiverList.receiver, new Intent(r.intent), r.resultCode, @@ -11177,6 +11293,9 @@ public final class ActivityManagerService extends ActivityManagerNative implemen r.receiver = null; r.curFilter = null; filter.receiverList.curBroadcast = null; + if (filter.receiverList.app != null) { + filter.receiverList.app.curReceiver = null; + } } } } @@ -11200,6 +11319,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen while (mParallelBroadcasts.size() > 0) { r = mParallelBroadcasts.remove(0); final int N = r.receivers.size(); + if (DEBUG_BROADCAST_LIGHT) Log.v(TAG, "Processing parallel broadcast " + + r); for (int i=0; i<N; i++) { Object target = r.receivers.get(i); if (DEBUG_BROADCAST) Log.v(TAG, @@ -11207,6 +11328,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen + target + ": " + r); deliverToRegisteredReceiver(r, (BroadcastFilter)target, false); } + if (DEBUG_BROADCAST_LIGHT) Log.v(TAG, "Done with parallel broadcast " + + r); } // Now take care of the next serialized one... @@ -11232,10 +11355,18 @@ public final class ActivityManagerService extends ActivityManagerNative implemen } } + boolean looped = false; + do { if (mOrderedBroadcasts.size() == 0) { // No more broadcasts pending, so all done! scheduleAppGcsLocked(); + if (looped) { + // If we had finished the last ordered broadcast, then + // make sure all processes have correct oom and sched + // adjustments. + updateOomAdjLocked(); + } return; } r = mOrderedBroadcasts.get(0); @@ -11292,9 +11423,13 @@ public final class ActivityManagerService extends ActivityManagerNative implemen if (DEBUG_BROADCAST) Log.v(TAG, "Cancelling BROADCAST_TIMEOUT_MSG"); mHandler.removeMessages(BROADCAST_TIMEOUT_MSG); + if (DEBUG_BROADCAST_LIGHT) Log.v(TAG, "Finished with ordered broadcast " + + r); + // ... and on to the next... mOrderedBroadcasts.remove(0); r = null; + looped = true; continue; } } while (r == null); @@ -11308,6 +11443,8 @@ public final class ActivityManagerService extends ActivityManagerNative implemen if (recIdx == 0) { r.dispatchTime = r.startTime; + if (DEBUG_BROADCAST_LIGHT) Log.v(TAG, "Processing ordered broadcast " + + r); if (DEBUG_BROADCAST) Log.v(TAG, "Submitting BROADCAST_TIMEOUT_MSG for " + (r.startTime + BROADCAST_TIMEOUT)); diff --git a/services/java/com/android/server/am/BroadcastRecord.java b/services/java/com/android/server/am/BroadcastRecord.java index 4057ae8..da55049 100644 --- a/services/java/com/android/server/am/BroadcastRecord.java +++ b/services/java/com/android/server/am/BroadcastRecord.java @@ -16,7 +16,7 @@ package com.android.server.am; -import android.app.IIntentReceiver; +import android.content.IIntentReceiver; import android.content.ComponentName; import android.content.Intent; import android.content.pm.ActivityInfo; diff --git a/services/java/com/android/server/am/HistoryRecord.java b/services/java/com/android/server/am/HistoryRecord.java index 944ea02..b3fc313 100644 --- a/services/java/com/android/server/am/HistoryRecord.java +++ b/services/java/com/android/server/am/HistoryRecord.java @@ -463,6 +463,12 @@ class HistoryRecord extends IApplicationToken.Stub { return false; } + if (service.mDidDexOpt) { + // Give more time since we were dexopting. + service.mDidDexOpt = false; + return false; + } + if (r.app.instrumentationClass == null) { service.appNotRespondingLocked(r.app, r, "keyDispatchingTimedOut"); } else { diff --git a/services/java/com/android/server/am/PendingIntentRecord.java b/services/java/com/android/server/am/PendingIntentRecord.java index 4381392..fa2a100 100644 --- a/services/java/com/android/server/am/PendingIntentRecord.java +++ b/services/java/com/android/server/am/PendingIntentRecord.java @@ -17,8 +17,8 @@ package com.android.server.am; import android.app.IActivityManager; -import android.app.IIntentSender; -import android.app.IIntentReceiver; +import android.content.IIntentSender; +import android.content.IIntentReceiver; import android.app.PendingIntent; import android.content.Intent; import android.os.Binder; diff --git a/services/java/com/android/server/am/ReceiverList.java b/services/java/com/android/server/am/ReceiverList.java index 0facefc..32c24c6 100644 --- a/services/java/com/android/server/am/ReceiverList.java +++ b/services/java/com/android/server/am/ReceiverList.java @@ -16,7 +16,7 @@ package com.android.server.am; -import android.app.IIntentReceiver; +import android.content.IIntentReceiver; import android.content.Intent; import android.os.Binder; import android.os.Bundle; diff --git a/services/java/com/android/server/am/UsageStatsService.java b/services/java/com/android/server/am/UsageStatsService.java index 866334b..2d58659 100755 --- a/services/java/com/android/server/am/UsageStatsService.java +++ b/services/java/com/android/server/am/UsageStatsService.java @@ -56,9 +56,9 @@ public final class UsageStatsService extends IUsageStats.Stub { private static final String TAG = "UsageStats"; // Current on-disk Parcel version - private static final int VERSION = 1004; + private static final int VERSION = 1005; - private static final int CHECKIN_VERSION = 3; + private static final int CHECKIN_VERSION = 4; private static final String FILE_PREFIX = "usage-"; @@ -82,7 +82,9 @@ public final class UsageStatsService extends IUsageStats.Stub { // this lock held. final Object mFileLock; // Order of locks is mFileLock followed by mStatsLock to avoid deadlocks - private String mResumedPkg; + private String mLastResumedPkg; + private String mLastResumedComp; + private boolean mIsResumed; private File mFile; private String mFileLeaf; //private File mBackupFile; @@ -92,11 +94,16 @@ public final class UsageStatsService extends IUsageStats.Stub { private int mLastWriteDay; static class TimeStats { + int count; int[] times = new int[NUM_LAUNCH_TIME_BINS]; TimeStats() { } + void incCount() { + count++; + } + void add(int val) { final int[] bins = LAUNCH_TIME_BINS; for (int i=0; i<NUM_LAUNCH_TIME_BINS-1; i++) { @@ -109,6 +116,7 @@ public final class UsageStatsService extends IUsageStats.Stub { } TimeStats(Parcel in) { + count = in.readInt(); final int[] localTimes = times; for (int i=0; i<NUM_LAUNCH_TIME_BINS; i++) { localTimes[i] = in.readInt(); @@ -116,6 +124,7 @@ public final class UsageStatsService extends IUsageStats.Stub { } void writeToParcel(Parcel out) { + out.writeInt(count); final int[] localTimes = times; for (int i=0; i<NUM_LAUNCH_TIME_BINS; i++) { out.writeInt(localTimes[i]); @@ -152,8 +161,10 @@ public final class UsageStatsService extends IUsageStats.Stub { } } - void updateResume() { - mLaunchCount ++; + void updateResume(boolean launched) { + if (launched) { + mLaunchCount ++; + } mResumedTime = SystemClock.elapsedRealtime(); } @@ -162,6 +173,15 @@ public final class UsageStatsService extends IUsageStats.Stub { mUsageTime += (mPausedTime - mResumedTime); } + void addLaunchCount(String comp) { + TimeStats times = mLaunchTimes.get(comp); + if (times == null) { + times = new TimeStats(); + mLaunchTimes.put(comp, times); + } + times.incCount(); + } + void addLaunchTime(String comp, int millis) { TimeStats times = mLaunchTimes.get(comp); if (times == null) { @@ -436,43 +456,70 @@ public final class UsageStatsService extends IUsageStats.Stub { public void noteResumeComponent(ComponentName componentName) { enforceCallingPermission(); String pkgName; - if ((componentName == null) || - ((pkgName = componentName.getPackageName()) == null)) { - return; - } - if ((mResumedPkg != null) && (mResumedPkg.equalsIgnoreCase(pkgName))) { - // Moving across activities in same package. just return - return; - } - if (localLOGV) Log.i(TAG, "started component:"+pkgName); synchronized (mStatsLock) { + if ((componentName == null) || + ((pkgName = componentName.getPackageName()) == null)) { + return; + } + + final boolean samePackage = pkgName.equals(mLastResumedPkg); + if (mIsResumed) { + if (samePackage) { + Log.w(TAG, "Something wrong here, didn't expect " + + pkgName + " to be resumed"); + return; + } + + if (mLastResumedPkg != null) { + // We last resumed some other package... just pause it now + // to recover. + Log.w(TAG, "Unexpected resume of " + pkgName + + " while already resumed in " + mLastResumedPkg); + PkgUsageStatsExtended pus = mStats.get(mLastResumedPkg); + if (pus != null) { + pus.updatePause(); + } + } + } + + final boolean sameComp = samePackage + && componentName.getClassName().equals(mLastResumedComp); + + mIsResumed = true; + mLastResumedPkg = pkgName; + mLastResumedComp = componentName.getClassName(); + + if (localLOGV) Log.i(TAG, "started component:" + pkgName); PkgUsageStatsExtended pus = mStats.get(pkgName); if (pus == null) { pus = new PkgUsageStatsExtended(); mStats.put(pkgName, pus); } - pus.updateResume(); + pus.updateResume(!samePackage); + if (!sameComp) { + pus.addLaunchCount(mLastResumedComp); + } } - mResumedPkg = pkgName; } public void notePauseComponent(ComponentName componentName) { enforceCallingPermission(); - String pkgName; - if ((componentName == null) || - ((pkgName = componentName.getPackageName()) == null)) { - return; - } - if ((mResumedPkg == null) || (!pkgName.equalsIgnoreCase(mResumedPkg))) { - Log.w(TAG, "Something wrong here, Didn't expect "+pkgName+" to be paused"); - return; - } - if (localLOGV) Log.i(TAG, "paused component:"+pkgName); - - // Persist current data to file if needed. - writeStatsToFile(false); synchronized (mStatsLock) { + String pkgName; + if ((componentName == null) || + ((pkgName = componentName.getPackageName()) == null)) { + return; + } + if (!mIsResumed) { + Log.w(TAG, "Something wrong here, didn't expect " + + pkgName + " to be paused"); + return; + } + mIsResumed = false; + + if (localLOGV) Log.i(TAG, "paused component:"+pkgName); + PkgUsageStatsExtended pus = mStats.get(pkgName); if (pus == null) { // Weird some error here @@ -481,6 +528,9 @@ public final class UsageStatsService extends IUsageStats.Stub { } pus.updatePause(); } + + // Persist current data to file if needed. + writeStatsToFile(false); } public void noteLaunchTime(ComponentName componentName, int millis) { @@ -631,9 +681,9 @@ public final class UsageStatsService extends IUsageStats.Stub { if (isCompactOutput) { sb.append("P:"); sb.append(pkgName); - sb.append(","); + sb.append(','); sb.append(pus.mLaunchCount); - sb.append(","); + sb.append(','); sb.append(pus.mUsageTime); sb.append('\n'); final int NC = pus.mLaunchTimes.size(); @@ -642,6 +692,8 @@ public final class UsageStatsService extends IUsageStats.Stub { sb.append("A:"); sb.append(ent.getKey()); TimeStats times = ent.getValue(); + sb.append(','); + sb.append(times.count); for (int i=0; i<NUM_LAUNCH_TIME_BINS; i++) { sb.append(","); sb.append(times.times[i]); @@ -665,25 +717,26 @@ public final class UsageStatsService extends IUsageStats.Stub { sb.append(" "); sb.append(ent.getKey()); TimeStats times = ent.getValue(); + sb.append(": "); + sb.append(times.count); + sb.append(" starts"); int lastBin = 0; - boolean first = true; for (int i=0; i<NUM_LAUNCH_TIME_BINS-1; i++) { if (times.times[i] != 0) { - sb.append(first ? ": " : ", "); + sb.append(", "); sb.append(lastBin); sb.append('-'); sb.append(LAUNCH_TIME_BINS[i]); - sb.append('='); + sb.append("ms="); sb.append(times.times[i]); - first = false; } lastBin = LAUNCH_TIME_BINS[i]; } if (times.times[NUM_LAUNCH_TIME_BINS-1] != 0) { - sb.append(first ? ": " : ", "); + sb.append(", "); sb.append(">="); sb.append(lastBin); - sb.append('='); + sb.append("ms="); sb.append(times.times[NUM_LAUNCH_TIME_BINS-1]); } sb.append('\n'); diff --git a/services/java/com/android/server/status/StatusBarPolicy.java b/services/java/com/android/server/status/StatusBarPolicy.java index b59fe54..059dc7b 100644 --- a/services/java/com/android/server/status/StatusBarPolicy.java +++ b/services/java/com/android/server/status/StatusBarPolicy.java @@ -707,6 +707,23 @@ public class StatusBarPolicy { b.setView(v); b.setIcon(android.R.drawable.ic_dialog_alert); b.setPositiveButton(android.R.string.ok, null); + + final Intent intent = new Intent(Intent.ACTION_POWER_USAGE_SUMMARY); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK + | Intent.FLAG_ACTIVITY_MULTIPLE_TASK + | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS + | Intent.FLAG_ACTIVITY_NO_HISTORY); + if (intent.resolveActivity(mContext.getPackageManager()) != null) { + b.setNegativeButton(com.android.internal.R.string.battery_low_why, + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + mContext.startActivity(intent); + if (mLowBatteryDialog != null) { + mLowBatteryDialog.dismiss(); + } + } + }); + } AlertDialog d = b.create(); d.setOnDismissListener(mLowBatteryListener); diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java index 9395d66..890f930 100644 --- a/telephony/java/android/telephony/SmsManager.java +++ b/telephony/java/android/telephony/SmsManager.java @@ -22,7 +22,6 @@ import android.os.ServiceManager; import android.text.TextUtils; import com.android.internal.telephony.EncodeException; -import com.android.internal.telephony.GsmAlphabet; import com.android.internal.telephony.ISms; import com.android.internal.telephony.IccConstants; import com.android.internal.telephony.SmsRawData; @@ -31,14 +30,12 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import static android.telephony.SmsMessage.ENCODING_7BIT; -import static android.telephony.SmsMessage.ENCODING_8BIT; -import static android.telephony.SmsMessage.ENCODING_16BIT; -import static android.telephony.SmsMessage.ENCODING_UNKNOWN; -import static android.telephony.SmsMessage.MAX_USER_DATA_BYTES; -import static android.telephony.SmsMessage.MAX_USER_DATA_BYTES_WITH_HEADER; -import static android.telephony.SmsMessage.MAX_USER_DATA_SEPTETS; -import static android.telephony.SmsMessage.MAX_USER_DATA_SEPTETS_WITH_HEADER; +/* + * TODO(code review): Curious question... Why are a lot of these + * methods not declared as static, since they do not seem to require + * any local object state? Assumedly this cannot be changed without + * interfering with the API... + */ /** * Manages SMS operations such as sending data, text, and pdu SMS messages. @@ -88,7 +85,7 @@ public final class SmsManager { } /** - * Divide a text message into several messages, none bigger than + * Divide a message text into several fragments, none bigger than * the maximum SMS message size. * * @param text the original message. Must not be null. @@ -96,40 +93,7 @@ public final class SmsManager { * comprise the original message */ public ArrayList<String> divideMessage(String text) { - int size = text.length(); - int[] params = SmsMessage.calculateLength(text, false); - /* SmsMessage.calculateLength returns an int[4] with: - * int[0] being the number of SMS's required, - * int[1] the number of code units used, - * int[2] is the number of code units remaining until the next message. - * int[3] is the encoding type that should be used for the message. - */ - int messageCount = params[0]; - int encodingType = params[3]; - ArrayList<String> result = new ArrayList<String>(messageCount); - - int start = 0; - int limit; - - if (messageCount > 1) { - limit = (encodingType == ENCODING_7BIT)? - MAX_USER_DATA_SEPTETS_WITH_HEADER: MAX_USER_DATA_BYTES_WITH_HEADER; - } else { - limit = (encodingType == ENCODING_7BIT)? - MAX_USER_DATA_SEPTETS: MAX_USER_DATA_BYTES; - } - - try { - while (start < size) { - int end = GsmAlphabet.findLimitIndex(text, start, limit, encodingType); - result.add(text.substring(start, end)); - start = end; - } - } - catch (EncodeException e) { - // ignore it. - } - return result; + return SmsMessage.fragmentText(text); } /** diff --git a/telephony/java/android/telephony/SmsMessage.java b/telephony/java/android/telephony/SmsMessage.java index b60da5a..775b034 100644 --- a/telephony/java/android/telephony/SmsMessage.java +++ b/telephony/java/android/telephony/SmsMessage.java @@ -17,12 +17,17 @@ package android.telephony; import android.os.Parcel; +import android.util.Log; import com.android.internal.telephony.GsmAlphabet; import com.android.internal.telephony.EncodeException; import com.android.internal.telephony.SmsHeader; import com.android.internal.telephony.SmsMessageBase; import com.android.internal.telephony.SmsMessageBase.SubmitPduBase; +import com.android.internal.telephony.SmsMessageBase.TextEncodingDetails; + +import java.lang.Math; +import java.util.ArrayList; import static android.telephony.TelephonyManager.PHONE_TYPE_CDMA; @@ -44,19 +49,41 @@ public class SmsMessage { UNKNOWN, CLASS_0, CLASS_1, CLASS_2, CLASS_3; } - /** Unknown encoding scheme (see TS 23.038) */ + /** + * TODO(cleanup): given that we now have more than one possible + * 7bit encoding, this result starts to look rather vague and + * maybe confusing... If this is just an indication of code unit + * size, maybe that is no problem. Otherwise, should we try to + * create an aggregate collection of GSM and CDMA encodings? CDMA + * contains a superset of the encodings we use (it does not + * support 8-bit GSM, but we also do not use that encoding + * currently)... We could get rid of these and directly reference + * the CDMA encoding definitions... + */ + + /** User data text encoding code unit size */ public static final int ENCODING_UNKNOWN = 0; - /** 7-bit encoding scheme (see TS 23.038) */ public static final int ENCODING_7BIT = 1; - /** 8-bit encoding scheme (see TS 23.038) */ public static final int ENCODING_8BIT = 2; - /** 16-bit encoding scheme (see TS 23.038) */ public static final int ENCODING_16BIT = 3; /** The maximum number of payload bytes per message */ public static final int MAX_USER_DATA_BYTES = 140; /** + * TODO(cleanup): It would be more flexible and less fragile to + * rewrite this (meaning get rid of the following constant) such + * that an actual UDH is taken into consideration (meaning its + * length is measured), allowing for messages that actually + * contain other UDH fields... Hence it is actually a shame to + * extend the API with this constant. If necessary, maybe define + * the size of such a header and let the math for calculating + * max_octets/septets be done elsewhere. And, while I am griping, + * if we use the word septet, we should use the word octet in + * corresponding places, not byte... + */ + + /** * The maximum number of payload bytes per message if a user data header * is present. This assumes the header only contains the * CONCATENATED_8_BIT_REFERENCE element. @@ -222,6 +249,15 @@ public class SmsMessage { } } + /* + * TODO(cleanup): It would make some sense if the result of + * preprocessing a message to determine the proper encoding (ie + * the resulting datastructure from calculateLength) could be + * passed as an argument to the actual final encoding function. + * This would better ensure that the logic behind size calculation + * actually matched the encoding. + */ + /** * Calculates the number of SMS's required to encode the message body and * the number of characters remaining until the next message. @@ -232,46 +268,76 @@ public class SmsMessage { * space chars. If false, and if the messageBody contains * non-7-bit encodable characters, length is calculated * using a 16-bit encoding. - * @return an int[4] with int[0] being the number of SMS's required, int[1] - * the number of code units used, and int[2] is the number of code - * units remaining until the next message. int[3] is the encoding - * type that should be used for the message. + * @return an int[4] with int[0] being the number of SMS's + * required, int[1] the number of code units used, and + * int[2] is the number of code units remaining until the + * next message. int[3] is an indicator of the encoding + * code unit size (see the ENCODING_* definitions in this + * class). */ public static int[] calculateLength(CharSequence msgBody, boolean use7bitOnly) { int activePhone = TelephonyManager.getDefault().getPhoneType(); + TextEncodingDetails ted = (PHONE_TYPE_CDMA == activePhone) ? + com.android.internal.telephony.gsm.SmsMessage.calculateLength(msgBody, use7bitOnly) : + com.android.internal.telephony.cdma.SmsMessage.calculateLength(msgBody, use7bitOnly); int ret[] = new int[4]; + ret[0] = ted.msgCount; + ret[1] = ted.codeUnitCount; + ret[2] = ted.codeUnitsRemaining; + ret[3] = ted.codeUnitSize; + return ret; + } - int septets = (PHONE_TYPE_CDMA == activePhone) ? - com.android.internal.telephony.cdma.SmsMessage.calc7bitEncodedLength(msgBody, - use7bitOnly) : - com.android.internal.telephony.gsm.SmsMessage.calc7bitEncodedLength(msgBody, - use7bitOnly); - if (septets != -1) { - ret[1] = septets; - if (septets > MAX_USER_DATA_SEPTETS) { - ret[0] = (septets / MAX_USER_DATA_SEPTETS_WITH_HEADER) + 1; - ret[2] = MAX_USER_DATA_SEPTETS_WITH_HEADER - - (septets % MAX_USER_DATA_SEPTETS_WITH_HEADER); - } else { - ret[0] = 1; - ret[2] = MAX_USER_DATA_SEPTETS - septets; - } - ret[3] = ENCODING_7BIT; + /** + * Divide a message text into several fragments, none bigger than + * the maximum SMS message text size. + * + * @param text text, must not be null. + * @return an <code>ArrayList</code> of strings that, in order, + * comprise the original msg text + */ + public static ArrayList<String> fragmentText(String text) { + int activePhone = TelephonyManager.getDefault().getPhoneType(); + TextEncodingDetails ted = (PHONE_TYPE_CDMA == activePhone) ? + com.android.internal.telephony.gsm.SmsMessage.calculateLength(text, false) : + com.android.internal.telephony.cdma.SmsMessage.calculateLength(text, false); + + // TODO(cleanup): The code here could be rolled into the logic + // below cleanly if these MAX_* constants were defined more + // flexibly... + + int limit; + if (ted.msgCount > 1) { + limit = (ted.codeUnitSize == ENCODING_7BIT) ? + MAX_USER_DATA_SEPTETS_WITH_HEADER : MAX_USER_DATA_BYTES_WITH_HEADER; } else { - int octets = msgBody.length() * 2; - ret[1] = msgBody.length(); - if (octets > MAX_USER_DATA_BYTES) { - ret[0] = (octets / MAX_USER_DATA_BYTES_WITH_HEADER) + 1; - ret[2] = (MAX_USER_DATA_BYTES_WITH_HEADER - - (octets % MAX_USER_DATA_BYTES_WITH_HEADER))/2; - } else { - ret[0] = 1; - ret[2] = (MAX_USER_DATA_BYTES - octets)/2; - } - ret[3] = ENCODING_16BIT; + limit = (ted.codeUnitSize == ENCODING_7BIT) ? + MAX_USER_DATA_SEPTETS : MAX_USER_DATA_BYTES; } - return ret; + int pos = 0; // Index in code units. + int textLen = text.length(); + ArrayList<String> result = new ArrayList<String>(ted.msgCount); + while (pos < textLen) { + int nextPos = 0; // Counts code units. + if (ted.codeUnitSize == ENCODING_7BIT) { + if (PHONE_TYPE_CDMA == activePhone) { + nextPos = pos + Math.min(limit, textLen - pos); + } else { + nextPos = GsmAlphabet.findGsmSeptetLimitIndex(text, pos, limit); + } + } else { // Assume unicode. + nextPos = pos + Math.min(limit / 2, textLen - pos); + } + if ((nextPos <= pos) || (nextPos > textLen)) { + Log.e(LOG_TAG, "fragmentText failed (" + pos + " >= " + nextPos + " or " + + nextPos + " >= " + textLen + ")"); + break; + } + result.add(text.substring(pos, nextPos)); + pos = nextPos; + } + return result; } /** diff --git a/telephony/java/com/android/internal/telephony/GsmAlphabet.java b/telephony/java/com/android/internal/telephony/GsmAlphabet.java index 8e2941b..e8095e1 100644 --- a/telephony/java/com/android/internal/telephony/GsmAlphabet.java +++ b/telephony/java/com/android/internal/telephony/GsmAlphabet.java @@ -576,52 +576,6 @@ public class GsmAlphabet { return size; } - /** - * Returns the index into <code>s</code> of the first character - * after <code>limit</code> octets have been reached, starting at - * index <code>start</code>. This is used when dividing messages - * in UCS2 encoding into units within the SMS message size limit. - * - * @param s source string - * @param start index of where to start counting septets - * @param limit maximum septets to include, - * e.g. <code>MAX_USER_DATA_BYTES</code> - * @return index of first character that won't fit, or the length - * of the entire string if everything fits - */ - public static int - findUCS2LimitIndex(String s, int start, int limit) { - int numCharToBeEncoded = s.length() - start; - return ((numCharToBeEncoded*2 > limit)? limit/2: numCharToBeEncoded) + start; - } - - /** - * Returns the index into <code>s</code> of the first character - * after <code>limit</code> septets/octets have been reached - * according to the <code>encodingType</code>, starting at - * index <code>start</code>. This is used when dividing messages - * units within the SMS message size limit. - * - * @param s source string - * @param start index of where to start counting septets - * @param limit maximum septets to include, - * e.g. <code>MAX_USER_DATA_BYTES</code> - * @return index of first character that won't fit, or the length - * of the entire string if everything fits - */ - public static int - findLimitIndex(String s, int start, int limit, int encodingType) throws EncodeException { - if (encodingType == SmsMessage.ENCODING_7BIT) { - return findGsmSeptetLimitIndex(s, start, limit); - } - else if (encodingType == SmsMessage.ENCODING_16BIT) { - return findUCS2LimitIndex(s, start, limit); - } - else { - throw new EncodeException("Unsupported encoding type: " + encodingType); - } - } - // Set in the static initializer private static int sGsmSpaceChar; diff --git a/telephony/java/com/android/internal/telephony/RIL.java b/telephony/java/com/android/internal/telephony/RIL.java index 07fc7c6..c6b7601 100644 --- a/telephony/java/com/android/internal/telephony/RIL.java +++ b/telephony/java/com/android/internal/telephony/RIL.java @@ -1268,7 +1268,11 @@ public final class RIL extends BaseCommands implements CommandsInterface { rr.mp.writeString(user); rr.mp.writeString(password); //TODO(): Add to the APN database, AuthType is set to CHAP/PAP - rr.mp.writeString("3"); + // 0 => Neither PAP nor CHAP will be performed, 3 => PAP / CHAP will be performed. + if (user != null) + rr.mp.writeString("3"); + else + rr.mp.writeString("0"); if (RILJ_LOGD) riljLog(rr.serialString() + "> " + requestToString(rr.mRequest) + " " + apn); diff --git a/telephony/java/com/android/internal/telephony/SmsMessageBase.java b/telephony/java/com/android/internal/telephony/SmsMessageBase.java index 4d32c35..3c7dd45 100644 --- a/telephony/java/com/android/internal/telephony/SmsMessageBase.java +++ b/telephony/java/com/android/internal/telephony/SmsMessageBase.java @@ -86,6 +86,38 @@ public abstract class SmsMessageBase { /** TP-Message-Reference - Message Reference of sent message. @hide */ public int messageRef; + /** + * For a specific text string, this object describes protocol + * properties of encoding it for transmission as message user + * data. + */ + public static class TextEncodingDetails { + /** + *The number of SMS's required to encode the text. + */ + public int msgCount; + + /** + * The number of code units consumed so far, where code units + * are basically characters in the encoding -- for example, + * septets for the standard ASCII and GSM encodings, and 16 + * bits for Unicode. + */ + public int codeUnitCount; + + /** + * How many code units are still available without spilling + * into an additional message. + */ + public int codeUnitsRemaining; + + /** + * The encoding code unit size (specified using + * android.telephony.SmsMessage ENCODING_*). + */ + public int codeUnitSize; + } + public static abstract class SubmitPduBase { public byte[] encodedScAddress; // Null if not applicable. public byte[] encodedMessage; diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java b/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java index 79e1cd6..2b4a700 100644 --- a/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java +++ b/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java @@ -34,6 +34,7 @@ import com.android.internal.telephony.SmsMessageBase; import com.android.internal.telephony.SMSDispatcher; import com.android.internal.telephony.cdma.SmsMessage; import com.android.internal.telephony.cdma.sms.SmsEnvelope; +import com.android.internal.telephony.cdma.sms.UserData; import com.android.internal.util.HexDump; import java.io.ByteArrayOutputStream; @@ -302,8 +303,12 @@ final class CdmaSMSDispatcher extends SMSDispatcher { deliveryIntent = deliveryIntents.get(i); } - SmsMessage.SubmitPdu submitPdu = SmsMessage.getSubmitPdu(scAddr, destAddr, - parts.get(i), deliveryIntent != null, smsHeader); + UserData uData = new UserData(); + uData.payloadStr = parts.get(i); + uData.userDataHeader = smsHeader; + + SmsMessage.SubmitPdu submitPdu = SmsMessage.getSubmitPdu(destAddr, + uData, deliveryIntent != null); sendSubmitPdu(submitPdu, sentIntent, deliveryIntent); } diff --git a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java index bbdd0dd..63d2c47 100644 --- a/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java +++ b/telephony/java/com/android/internal/telephony/cdma/SmsMessage.java @@ -357,14 +357,18 @@ public class SmsMessage extends SmsMessageBase { } /** - * Calculate the number of septets needed to encode the message. + * Get an SMS-SUBMIT PDU for a data message to a destination address & port * - * @param messageBody the message to encode - * @param force ignore (but still count) illegal characters if true - * @return septet count, or -1 on failure + * @param destAddr the address of the destination for the message + * @param userDara the data for the message + * @param statusReportRequested Indicates whether a report is requested for this message. + * @return a <code>SubmitPdu</code> containing the encoded SC + * address, if applicable, and the encoded message. + * Returns null on encode error. */ - public static int calc7bitEncodedLength(CharSequence msgBody, boolean force) { - return BearerData.calc7bitEncodedLength(msgBody.toString(), force); + public static SubmitPdu getSubmitPdu(String destAddr, UserData userData, + boolean statusReportRequested) { + return privateGetSubmitPdu(destAddr, statusReportRequested, userData); } /** @@ -442,6 +446,18 @@ public class SmsMessage extends SmsMessageBase { } /** + * Calculate the number of septets needed to encode the message. + * + * @param messageBody the message to encode + * @param use7bitOnly ignore (but still count) illegal characters if true + * @return TextEncodingDetails + */ + public static TextEncodingDetails calculateLength(CharSequence messageBody, + boolean use7bitOnly) { + return BearerData.calcTextEncodingDetails(messageBody.toString(), use7bitOnly); + } + + /** * Returns the teleservice type of the message. * @return the teleservice: * {@link com.android.internal.telephony.cdma.sms.SmsEnvelope#TELESERVICE_NOT_SET}, @@ -627,12 +643,15 @@ public class SmsMessage extends SmsMessageBase { bearerData.userData = userData; bearerData.hasUserDataHeader = (userData.userDataHeader != null); + int teleservice = bearerData.hasUserDataHeader ? + SmsEnvelope.TELESERVICE_WEMT : SmsEnvelope.TELESERVICE_WMT; + byte[] encodedBearerData = BearerData.encode(bearerData); if (encodedBearerData == null) return null; SmsEnvelope envelope = new SmsEnvelope(); envelope.messageType = SmsEnvelope.MESSAGE_TYPE_POINT_TO_POINT; - envelope.teleService = SmsEnvelope.TELESERVICE_WMT; + envelope.teleService = teleservice; envelope.destAddress = destAddr; envelope.bearerReply = RETURN_ACK; envelope.bearerData = encodedBearerData; diff --git a/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java b/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java index 3c45aa4..a835dee 100644 --- a/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java +++ b/telephony/java/com/android/internal/telephony/cdma/sms/BearerData.java @@ -17,6 +17,7 @@ package com.android.internal.telephony.cdma.sms; import android.util.Log; +import android.util.SparseIntArray; import android.telephony.SmsMessage; @@ -26,6 +27,7 @@ import com.android.internal.telephony.IccUtils; import com.android.internal.telephony.GsmAlphabet; import com.android.internal.telephony.SmsHeader; import com.android.internal.telephony.cdma.sms.UserData; +import com.android.internal.telephony.SmsMessageBase.TextEncodingDetails; import com.android.internal.util.HexDump; import com.android.internal.util.BitwiseInputStream; @@ -35,7 +37,7 @@ import com.android.internal.util.BitwiseOutputStream; /** * An object to encode and decode CDMA SMS bearer data. */ -public final class BearerData{ +public final class BearerData { private final static String LOG_TAG = "SMS"; /** @@ -385,59 +387,64 @@ public final class BearerData{ outStream.skip(3); } - private static class SeptetData { - byte data[]; - int septetCount; + private static int countAsciiSeptets(CharSequence msg, boolean force) { + int msgLen = msg.length(); + if (force) return msgLen; + for (int i = 0; i < msgLen; i++) { + if (UserData.charToAscii.get(msg.charAt(i), -1) == -1) { + return -1; + } + } + return msgLen; + } - SeptetData(byte[] data, int septetCount) { - this.data = data; - this.septetCount = septetCount; + /** + * Calculate the message text encoding length, fragmentation, and other details. + * + * @param force ignore (but still count) illegal characters if true + * @return septet count, or -1 on failure + */ + public static TextEncodingDetails calcTextEncodingDetails(CharSequence msg, + boolean force7BitEncoding) { + TextEncodingDetails ted; + int septets = countAsciiSeptets(msg, force7BitEncoding); + if (septets != -1 && septets <= SmsMessage.MAX_USER_DATA_SEPTETS) { + ted = new TextEncodingDetails(); + ted.msgCount = 1; + ted.codeUnitCount = septets; + ted.codeUnitsRemaining = SmsMessage.MAX_USER_DATA_SEPTETS - septets; + ted.codeUnitSize = SmsMessage.ENCODING_7BIT; + } else { + ted = com.android.internal.telephony.gsm.SmsMessage.calculateLength( + msg, force7BitEncoding); } + return ted; } - private static SeptetData encode7bitAscii(String msg, boolean force) + private static byte[] encode7bitAscii(String msg, boolean force) throws CodingException { try { BitwiseOutputStream outStream = new BitwiseOutputStream(msg.length()); - byte[] expandedData = msg.getBytes("US-ASCII"); - for (int i = 0; i < expandedData.length; i++) { - int charCode = expandedData[i]; - // Test ourselves for ASCII membership, since Java seems not to care. - if ((charCode < UserData.PRINTABLE_ASCII_MIN_INDEX) || - (charCode > UserData.PRINTABLE_ASCII_MAX_INDEX)) { + int msgLen = msg.length(); + for (int i = 0; i < msgLen; i++) { + int charCode = UserData.charToAscii.get(msg.charAt(i), -1); + if (charCode == -1) { if (force) { outStream.write(7, UserData.UNENCODABLE_7_BIT_CHAR); } else { - throw new CodingException("illegal ASCII code (" + charCode + ")"); + throw new CodingException("cannot ASCII encode (" + msg.charAt(i) + ")"); } } else { - outStream.write(7, expandedData[i]); + outStream.write(7, charCode); } } - return new SeptetData(outStream.toByteArray(), expandedData.length); - } catch (java.io.UnsupportedEncodingException ex) { - throw new CodingException("7bit ASCII encode failed: " + ex); + return outStream.toByteArray(); } catch (BitwiseOutputStream.AccessException ex) { throw new CodingException("7bit ASCII encode failed: " + ex); } } - /** - * Calculate the number of septets needed to encode the message. - * - * @param force ignore (but still count) illegal characters if true - * @return septet count, or -1 on failure - */ - public static int calc7bitEncodedLength(String msg, boolean force) { - try { - SeptetData data = encode7bitAscii(msg, force); - return data.septetCount; - } catch (CodingException ex) { - return -1; - } - } - private static byte[] encodeUtf16(String msg) throws CodingException { @@ -452,8 +459,10 @@ public final class BearerData{ throws CodingException { try { - /** - * TODO(cleanup): find some way to do this without the copy. + /* + * TODO(cleanup): It would be nice if GsmAlphabet provided + * an option to produce just the data without prepending + * the length. */ byte []fullData = GsmAlphabet.stringToGsm7BitPacked(msg); byte []data = new byte[fullData.length - 1]; @@ -470,54 +479,65 @@ public final class BearerData{ throws CodingException { byte[] headerData = null; + // TODO: if there is a header, meaning EMS mode, we probably + // also want the total UD length prior to the UDH length... if (uData.userDataHeader != null) headerData = SmsHeader.toByteArray(uData.userDataHeader); int headerDataLen = (headerData == null) ? 0 : headerData.length + 1; // + length octet byte[] payloadData; + int codeUnitCount; if (uData.msgEncodingSet) { if (uData.msgEncoding == UserData.ENCODING_OCTET) { if (uData.payload == null) { Log.e(LOG_TAG, "user data with octet encoding but null payload"); - // TODO(code_review): reasonable for fail case? or maybe bail on encoding? payloadData = new byte[0]; + codeUnitCount = 0; } else { payloadData = uData.payload; + codeUnitCount = uData.payload.length; } } else { if (uData.payloadStr == null) { Log.e(LOG_TAG, "non-octet user data with null payloadStr"); - // TODO(code_review): reasonable for fail case? or maybe bail on encoding? uData.payloadStr = ""; } if (uData.msgEncoding == UserData.ENCODING_GSM_7BIT_ALPHABET) { payloadData = encode7bitGsm(uData.payloadStr); + codeUnitCount = (payloadData.length * 8) / 7; } else if (uData.msgEncoding == UserData.ENCODING_7BIT_ASCII) { - SeptetData septetData = encode7bitAscii(uData.payloadStr, true); - payloadData = septetData.data; + payloadData = encode7bitAscii(uData.payloadStr, true); + codeUnitCount = uData.payloadStr.length(); } else if (uData.msgEncoding == UserData.ENCODING_UNICODE_16) { payloadData = encodeUtf16(uData.payloadStr); + codeUnitCount = uData.payloadStr.length(); } else { throw new CodingException("unsupported user data encoding (" + uData.msgEncoding + ")"); } - uData.numFields = uData.payloadStr.length(); } } else { if (uData.payloadStr == null) { Log.e(LOG_TAG, "user data with null payloadStr"); - // TODO(code_review): reasonable for fail case? or maybe bail on encoding? uData.payloadStr = ""; } try { - SeptetData septetData = encode7bitAscii(uData.payloadStr, false); - payloadData = septetData.data; - uData.msgEncoding = UserData.ENCODING_7BIT_ASCII; + if (headerData == null) { + payloadData = encode7bitAscii(uData.payloadStr, false); + codeUnitCount = uData.payloadStr.length(); + uData.msgEncoding = UserData.ENCODING_7BIT_ASCII; + } else { + // If there is a header, we are in EMS mode, in + // which case we use GSM encodings. + payloadData = encode7bitGsm(uData.payloadStr); + codeUnitCount = (payloadData.length * 8) / 7; + uData.msgEncoding = UserData.ENCODING_GSM_7BIT_ALPHABET; + } } catch (CodingException ex) { payloadData = encodeUtf16(uData.payloadStr); + codeUnitCount = uData.payloadStr.length(); uData.msgEncoding = UserData.ENCODING_UNICODE_16; } uData.msgEncodingSet = true; - uData.numFields = uData.payloadStr.length(); } int totalLength = payloadData.length + headerDataLen; @@ -526,6 +546,7 @@ public final class BearerData{ " > " + SmsMessage.MAX_USER_DATA_BYTES + " bytes)"); } + uData.numFields = codeUnitCount; uData.payload = new byte[totalLength]; if (headerData != null) { uData.payload[0] = (byte)headerData.length; diff --git a/telephony/java/com/android/internal/telephony/cdma/sms/UserData.java b/telephony/java/com/android/internal/telephony/cdma/sms/UserData.java index 8d4e769..d8a48cc 100644 --- a/telephony/java/com/android/internal/telephony/cdma/sms/UserData.java +++ b/telephony/java/com/android/internal/telephony/cdma/sms/UserData.java @@ -16,6 +16,8 @@ package com.android.internal.telephony.cdma.sms; +import android.util.SparseIntArray; + import com.android.internal.telephony.SmsHeader; import com.android.internal.util.HexDump; @@ -40,6 +42,10 @@ public class UserData { /** * IA5 data encoding character mappings. * (See CCITT Rec. T.50 Tables 1 and 3) + * + * Note this mapping is the the same as for printable ASCII + * characters, with a 0x20 offset, meaning that the ASCII SPACE + * character occurs with code 0x20. */ public static final char[] IA5_MAP = { ' ', '!', '"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.', '/', @@ -61,7 +67,16 @@ public class UserData { * Only elements between these indices in the ASCII table are printable. */ public static final int PRINTABLE_ASCII_MIN_INDEX = 0x20; - public static final int PRINTABLE_ASCII_MAX_INDEX = 0x7F; + public static final int ASCII_LF_INDEX = 0x0A; + public static final int ASCII_CR_INDEX = 0x0D; + public static final SparseIntArray charToAscii = new SparseIntArray(); + static { + for (int i = 0; i < IA5_MAP.length; i++) { + charToAscii.put(IA5_MAP[i], PRINTABLE_ASCII_MIN_INDEX + i); + } + charToAscii.put('\r', ASCII_LF_INDEX); + charToAscii.put('\n', ASCII_CR_INDEX); + } /** * Mapping for IA5 values less than 32 are flow control signals diff --git a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java index a15bbdf..f1207e4 100644 --- a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java +++ b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java @@ -26,6 +26,7 @@ import com.android.internal.telephony.EncodeException; import com.android.internal.telephony.GsmAlphabet; import com.android.internal.telephony.SmsHeader; import com.android.internal.telephony.SmsMessageBase; +import com.android.internal.telephony.SmsMessageBase.TextEncodingDetails; import java.io.ByteArrayOutputStream; import java.io.UnsupportedEncodingException; @@ -742,17 +743,39 @@ public class SmsMessage extends SmsMessageBase{ /** * Calculate the number of septets needed to encode the message. * - * @param messageBody the message to encode - * @param force ignore (but still count) illegal characters if true - * @return septet count, or -1 on failure + * @param msgBody the message to encode + * @param use7bitOnly ignore (but still count) illegal characters if true + * @return TextEncodingDetails */ - public static int calc7bitEncodedLength(CharSequence messageBody, boolean force) { + public static TextEncodingDetails calculateLength(CharSequence msgBody, + boolean use7bitOnly) { + TextEncodingDetails ted = new TextEncodingDetails(); try { - return GsmAlphabet.countGsmSeptets(messageBody, !force); + int septets = GsmAlphabet.countGsmSeptets(msgBody, !use7bitOnly); + ted.codeUnitCount = septets; + if (septets > MAX_USER_DATA_SEPTETS) { + ted.msgCount = (septets / MAX_USER_DATA_SEPTETS_WITH_HEADER) + 1; + ted.codeUnitsRemaining = MAX_USER_DATA_SEPTETS_WITH_HEADER + - (septets % MAX_USER_DATA_SEPTETS_WITH_HEADER); + } else { + ted.msgCount = 1; + ted.codeUnitsRemaining = MAX_USER_DATA_SEPTETS - septets; + } + ted.codeUnitSize = ENCODING_7BIT; } catch (EncodeException ex) { - /* Just fall through to the -1 error result below. */ + int octets = msgBody.length() * 2; + ted.codeUnitCount = msgBody.length(); + if (octets > MAX_USER_DATA_BYTES) { + ted.msgCount = (octets / MAX_USER_DATA_BYTES_WITH_HEADER) + 1; + ted.codeUnitsRemaining = (MAX_USER_DATA_BYTES_WITH_HEADER + - (octets % MAX_USER_DATA_BYTES_WITH_HEADER))/2; + } else { + ted.msgCount = 1; + ted.codeUnitsRemaining = (MAX_USER_DATA_BYTES - octets)/2; + } + ted.codeUnitSize = ENCODING_16BIT; } - return -1; + return ted; } /** {@inheritDoc} */ diff --git a/test-runner/android/test/mock/MockContext.java b/test-runner/android/test/mock/MockContext.java index 9e0cf2c..efc4880 100644 --- a/test-runner/android/test/mock/MockContext.java +++ b/test-runner/android/test/mock/MockContext.java @@ -24,6 +24,7 @@ import android.content.IntentFilter; import android.content.BroadcastReceiver; import android.content.ServiceConnection; import android.content.SharedPreferences; +import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.res.AssetManager; import android.content.res.Resources; @@ -100,6 +101,11 @@ public class MockContext extends Context { } @Override + public ApplicationInfo getApplicationInfo() { + throw new UnsupportedOperationException(); + } + + @Override public String getPackageResourcePath() { throw new UnsupportedOperationException(); } diff --git a/test-runner/android/test/mock/MockPackageManager.java b/test-runner/android/test/mock/MockPackageManager.java index f98a251..63a177e 100644 --- a/test-runner/android/test/mock/MockPackageManager.java +++ b/test-runner/android/test/mock/MockPackageManager.java @@ -16,10 +16,10 @@ package android.test.mock; -import android.app.PendingIntent; import android.content.ComponentName; import android.content.Intent; import android.content.IntentFilter; +import android.content.IntentSender; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.IPackageDeleteObserver; @@ -62,11 +62,6 @@ public class MockPackageManager extends PackageManager { } @Override - public ResolveInfo resolveActivity(Intent intent, int flags, String packageName) { - throw new UnsupportedOperationException(); - } - - @Override public int[] getPackageGids(String packageName) throws NameNotFoundException { throw new UnsupportedOperationException(); } @@ -328,13 +323,13 @@ public class MockPackageManager extends PackageManager { long idealStorageSize, IPackageDataObserver observer) { throw new UnsupportedOperationException(); } - + /** * @hide - to match hiding in superclass */ @Override public void freeStorage( - long idealStorageSize, PendingIntent onFinishedIntent) { + long idealStorageSize, IntentSender pi) { throw new UnsupportedOperationException(); } diff --git a/tests/AndroidTests/src/com/android/unit_tests/AppCacheTest.java b/tests/AndroidTests/src/com/android/unit_tests/AppCacheTest.java index 3daa8ab..fb1b9ad 100755 --- a/tests/AndroidTests/src/com/android/unit_tests/AppCacheTest.java +++ b/tests/AndroidTests/src/com/android/unit_tests/AppCacheTest.java @@ -507,7 +507,7 @@ public class AppCacheTest extends AndroidTestCase { try { // Spin lock waiting for call back synchronized(r) { - getPm().freeStorage(idealStorageSize, pi); + getPm().freeStorage(idealStorageSize, pi.getIntentSender()); long waitTime = 0; while(!r.isDone() && (waitTime < MAX_WAIT_TIME)) { r.wait(WAIT_TIME_INCR); diff --git a/tests/AndroidTests/src/com/android/unit_tests/CdmaSmsTest.java b/tests/AndroidTests/src/com/android/unit_tests/CdmaSmsTest.java index 16aca4d..b2529811 100644 --- a/tests/AndroidTests/src/com/android/unit_tests/CdmaSmsTest.java +++ b/tests/AndroidTests/src/com/android/unit_tests/CdmaSmsTest.java @@ -103,6 +103,9 @@ public class CdmaSmsTest extends AndroidTestCase { assertEquals(userData.msgEncoding, revBearerData.userData.msgEncoding); assertEquals(userData.payloadStr.length(), revBearerData.userData.numFields); assertEquals(userData.payloadStr, revBearerData.userData.payloadStr); + userData.payloadStr = "More @ testing\nis great^|^~woohoo"; + revBearerData = BearerData.decode(BearerData.encode(bearerData)); + assertEquals(userData.payloadStr, revBearerData.userData.payloadStr); } @SmallTest diff --git a/tests/DumpRenderTree/assets/run_layout_tests.py b/tests/DumpRenderTree/assets/run_layout_tests.py index 50ccb24..49165d0 100755 --- a/tests/DumpRenderTree/assets/run_layout_tests.py +++ b/tests/DumpRenderTree/assets/run_layout_tests.py @@ -131,7 +131,7 @@ def CompareResults(ref_dir, results_dir): result_file_name = "layout_tests_" + f + ".txt" DiffResults(f, os.path.join(results_dir, result_file_name), os.path.join(ref_dir, result_file_name), diff_result, - False, files != "passed") + False, f != "passed") logging.info("Detailed diffs are in " + diff_result) def main(options, args): diff --git a/tests/backup/src/com/android/backuptest/BackupTestActivity.java b/tests/backup/src/com/android/backuptest/BackupTestActivity.java index af7dfd4..f0c3f93 100644 --- a/tests/backup/src/com/android/backuptest/BackupTestActivity.java +++ b/tests/backup/src/com/android/backuptest/BackupTestActivity.java @@ -17,14 +17,17 @@ package com.android.backuptest; import android.app.ListActivity; +import android.backup.BackupDataInput; +import android.backup.BackupDataOutput; import android.backup.BackupManager; +import android.backup.FileBackupHelper; +import android.backup.FileRestoreHelper; +import android.backup.RestoreHelperDispatcher; import android.content.Intent; import android.content.SharedPreferences; -import android.net.Uri; import android.os.Bundle; import android.os.Handler; -import android.os.PowerManager; -import android.os.SystemClock; +import android.os.ParcelFileDescriptor; import android.util.Log; import android.view.View; import android.widget.ArrayAdapter; @@ -32,6 +35,10 @@ import android.widget.ListView; import android.widget.Toast; import java.io.BufferedReader; +import java.io.File; +import java.io.FileOutputStream; +import java.io.FileInputStream; +import java.io.FileNotFoundException; import java.io.InputStreamReader; import java.io.IOException; import java.io.PrintStream; @@ -123,6 +130,48 @@ public class BackupTestActivity extends ListActivity BackupManager bm = new BackupManager(BackupTestActivity.this); bm.dataChanged(); } + }, + new Test("Backup Helpers") { + void run() { + try { + writeFile("a", "a\naa", MODE_PRIVATE); + writeFile("empty", "", MODE_PRIVATE); + + ParcelFileDescriptor state = ParcelFileDescriptor.open( + new File(getFilesDir(), "state"), + ParcelFileDescriptor.MODE_READ_WRITE|ParcelFileDescriptor.MODE_CREATE| + ParcelFileDescriptor.MODE_TRUNCATE); + FileBackupHelper h = new FileBackupHelper(BackupTestActivity.this, + "FileBackupHelper"); + FileOutputStream dataFile = openFileOutput("backup_test", MODE_WORLD_READABLE); + BackupDataOutput data = new BackupDataOutput(dataFile.getFD()); + h.performBackup(null, data, state, new String[] { "a", "empty" }); + dataFile.close(); + state.close(); + } catch (IOException ex) { + throw new RuntimeException(ex); + } + } + }, + new Test("Restore Helpers") { + void run() { + try { + RestoreHelperDispatcher dispatch = new RestoreHelperDispatcher(); + dispatch.addHelper("FileBackupHelper", + new FileRestoreHelper(BackupTestActivity.this)); + FileInputStream dataFile = openFileInput("backup_test"); + BackupDataInput data = new BackupDataInput(dataFile.getFD()); + ParcelFileDescriptor state = ParcelFileDescriptor.open( + new File(getFilesDir(), "restore_state"), + ParcelFileDescriptor.MODE_READ_WRITE|ParcelFileDescriptor.MODE_CREATE| + ParcelFileDescriptor.MODE_TRUNCATE); + dispatch.dispatch(data, state); + dataFile.close(); + state.close(); + } catch (IOException ex) { + throw new RuntimeException(ex); + } + } } }; @@ -154,5 +203,14 @@ public class BackupTestActivity extends ListActivity t.run(); } + void writeFile(String name, String contents, int mode) { + try { + PrintStream out = new PrintStream(openFileOutput(name, mode)); + out.print(contents); + out.close(); + } catch (FileNotFoundException ex) { + throw new RuntimeException(ex); + } + } } diff --git a/tests/backup/src/com/android/backuptest/BackupTestAgent.java b/tests/backup/src/com/android/backuptest/BackupTestAgent.java index a370d69..0da4151 100644 --- a/tests/backup/src/com/android/backuptest/BackupTestAgent.java +++ b/tests/backup/src/com/android/backuptest/BackupTestAgent.java @@ -17,28 +17,45 @@ package com.android.backuptest; import android.app.BackupAgent; +import android.backup.BackupDataInput; import android.backup.BackupDataOutput; import android.backup.FileBackupHelper; +import android.backup.FileRestoreHelper; +import android.backup.RestoreHelperDispatcher; import android.os.ParcelFileDescriptor; import android.util.Log; +import java.io.IOException; + public class BackupTestAgent extends BackupAgent { static final String TAG = "BackupTestAgent"; + static final String SHARED_PREFS = "shared_prefs"; + static final String DATA_FILES = "data_files"; + static final String[] FILES = new String[] { + BackupTestActivity.FILE_NAME + }; + @Override public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data, ParcelFileDescriptor newState) { Log.d(TAG, "onBackup"); - FileBackupHelper helper = new FileBackupHelper(this); - helper.performBackup(oldState, data, newState, new String[] { - BackupTestActivity.FILE_NAME - }); + + (new FileBackupHelper(this, DATA_FILES)).performBackup(oldState, data, newState, FILES); } @Override - public void onRestore(ParcelFileDescriptor data, ParcelFileDescriptor newState) { + public void onRestore(BackupDataInput data, ParcelFileDescriptor newState) + throws IOException { Log.d(TAG, "onRestore"); + + RestoreHelperDispatcher dispatch = new RestoreHelperDispatcher(); + + // dispatch.addHelper(SHARED_PREFS, new SharedPrefsRestoreHelper(this)); + dispatch.addHelper(DATA_FILES, new FileRestoreHelper(this)); + + dispatch.dispatch(data, newState); } } diff --git a/tests/permission/src/com/android/framework/permission/tests/HardwareServicePermissionTest.java b/tests/permission/src/com/android/framework/permission/tests/HardwareServicePermissionTest.java new file mode 100644 index 0000000..dc6860a --- /dev/null +++ b/tests/permission/src/com/android/framework/permission/tests/HardwareServicePermissionTest.java @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.framework.permission.tests; + +import junit.framework.TestCase; + +import android.os.Binder; +import android.os.IHardwareService; +import android.os.RemoteException; +import android.os.ServiceManager; + +/** + * Verify that Hardware apis cannot be called without required permissions. + */ +public class HardwareServicePermissionTest extends TestCase { + + private IHardwareService mHardwareService; + + @Override + protected void setUp() throws Exception { + mHardwareService = IHardwareService.Stub.asInterface( + ServiceManager.getService("hardware")); + } + + /** + * Test that calling {@link android.os.IHardwareService#vibrate(long)} requires permissions. + * <p>Tests permission: + * {@link android.Manifest.permission#VIBRATE} + * @throws RemoteException + */ + public void testVibrate() throws RemoteException { + try { + mHardwareService.vibrate(2000); + fail("vibrate did not throw SecurityException as expected"); + } catch (SecurityException e) { + // expected + } + } + + /** + * Test that calling {@link android.os.IHardwareService#vibratePattern(long[], + * int, android.os.IBinder)} requires permissions. + * <p>Tests permission: + * {@link android.Manifest.permission#VIBRATE} + * @throws RemoteException + */ + public void testVibratePattern() throws RemoteException { + try { + mHardwareService.vibratePattern(new long[] {0}, 0, new Binder()); + fail("vibratePattern did not throw SecurityException as expected"); + } catch (SecurityException e) { + // expected + } + } + + /** + * Test that calling {@link android.os.IHardwareService#cancelVibrate()} requires permissions. + * <p>Tests permission: + * {@link android.Manifest.permission#VIBRATE} + * @throws RemoteException + */ + public void testCancelVibrate() throws RemoteException { + try { + mHardwareService.cancelVibrate(); + fail("cancelVibrate did not throw SecurityException as expected"); + } catch (SecurityException e) { + // expected + } + } + + /** + * Test that calling {@link android.os.IHardwareService#setFlashlightEnabled(boolean)} + * requires permissions. + * <p>Tests permissions: + * {@link android.Manifest.permission#HARDWARE_TEST} + * {@link android.Manifest.permission#FLASHLIGHT} + * @throws RemoteException + */ + public void testSetFlashlightEnabled() throws RemoteException { + try { + mHardwareService.setFlashlightEnabled(true); + fail("setFlashlightEnabled did not throw SecurityException as expected"); + } catch (SecurityException e) { + // expected + } + } + + /** + * Test that calling {@link android.os.IHardwareService#enableCameraFlash(int)} requires + * permissions. + * <p>Tests permission: + * {@link android.Manifest.permission#HARDWARE_TEST} + * {@link android.Manifest.permission#CAMERA} + * @throws RemoteException + */ + public void testEnableCameraFlash() throws RemoteException { + try { + mHardwareService.enableCameraFlash(100); + fail("enableCameraFlash did not throw SecurityException as expected"); + } catch (SecurityException e) { + // expected + } + } + + /** + * Test that calling {@link android.os.IHardwareService#setBacklights(int)} requires + * permissions. + * <p>Tests permission: + * {@link android.Manifest.permission#HARDWARE_TEST} + * @throws RemoteException + */ + public void testSetBacklights() throws RemoteException { + try { + mHardwareService.setBacklights(0); + fail("setBacklights did not throw SecurityException as expected"); + } catch (SecurityException e) { + // expected + } + } +} diff --git a/tests/permission/src/com/android/framework/permission/tests/SmsManagerPermissionTest.java b/tests/permission/src/com/android/framework/permission/tests/SmsManagerPermissionTest.java new file mode 100644 index 0000000..273943f --- /dev/null +++ b/tests/permission/src/com/android/framework/permission/tests/SmsManagerPermissionTest.java @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.framework.permission.tests; + +import java.util.ArrayList; + +import android.telephony.SmsManager; +import android.test.AndroidTestCase; +import android.test.suitebuilder.annotation.SmallTest; + +/** + * Verify that SmsManager apis cannot be called without required permissions. + */ +public class SmsManagerPermissionTest extends AndroidTestCase { + + private static final String MSG_CONTENTS = "hi"; + private static final short DEST_PORT = (short)1004; + private static final String DEST_NUMBER = "4567"; + private static final String SRC_NUMBER = "1234"; + + /** + * Verify that SmsManager.sendTextMessage requires permissions. + * <p>Tests Permission: + * {@link android.Manifest.permission#SEND_SMS}. + */ + @SmallTest + public void testSendTextMessage() { + try { + SmsManager.getDefault().sendTextMessage(SRC_NUMBER, DEST_NUMBER, MSG_CONTENTS, null, + null); + fail("SmsManager.sendTextMessage did not throw SecurityException as expected"); + } catch (SecurityException e) { + // expected + } + } + + /** + * Verify that SmsManager.sendDataMessage requires permissions. + * <p>Tests Permission: + * {@link android.Manifest.permission#SEND_SMS}. + */ + @SmallTest + public void testSendDataMessage() { + try { + SmsManager.getDefault().sendDataMessage(SRC_NUMBER, DEST_NUMBER, DEST_PORT, + MSG_CONTENTS.getBytes(), null, null); + fail("SmsManager.sendDataMessage did not throw SecurityException as expected"); + } catch (SecurityException e) { + // expected + } + } + + /** + * Verify that SmsManager.sendMultipartMessage requires permissions. + * <p>Tests Permission: + * {@link android.Manifest.permission#SEND_MMS}. + */ + @SmallTest + public void testSendMultipartMessage() { + try { + ArrayList<String> msgParts = new ArrayList<String>(2); + msgParts.add(MSG_CONTENTS); + msgParts.add("foo"); + SmsManager.getDefault().sendMultipartTextMessage(SRC_NUMBER, DEST_NUMBER, msgParts, + null, null); + fail("SmsManager.sendMultipartTextMessage did not throw SecurityException as expected"); + } catch (SecurityException e) { + // expected + } + } +} diff --git a/tools/aapt/Bundle.h b/tools/aapt/Bundle.h index 9e712b8..a671bd7 100644 --- a/tools/aapt/Bundle.h +++ b/tools/aapt/Bundle.h @@ -37,6 +37,7 @@ public: mForce(false), mGrayscaleTolerance(0), mMakePackageDirs(false), mUpdate(false), mExtending(false), mRequireLocalization(false), mPseudolocalize(false), + mValues(false), mCompressionMethod(0), mOutputAPKFile(NULL), mAssetSourceDir(NULL), mAndroidManifestFile(NULL), mPublicOutputFile(NULL), @@ -75,6 +76,8 @@ public: void setRequireLocalization(bool val) { mRequireLocalization = val; } bool getPseudolocalize(void) const { return mPseudolocalize; } void setPseudolocalize(bool val) { mPseudolocalize = val; } + bool getValues(void) const { return mValues; } + void setValues(bool val) { mValues = val; } int getCompressionMethod(void) const { return mCompressionMethod; } void setCompressionMethod(int val) { mCompressionMethod = val; } const char* getOutputAPKFile() const { return mOutputAPKFile; } @@ -154,6 +157,7 @@ private: bool mExtending; bool mRequireLocalization; bool mPseudolocalize; + bool mValues; int mCompressionMethod; const char* mOutputAPKFile; const char* mAssetSourceDir; diff --git a/tools/aapt/Command.cpp b/tools/aapt/Command.cpp index 0e889f5..dcda379 100644 --- a/tools/aapt/Command.cpp +++ b/tools/aapt/Command.cpp @@ -198,7 +198,7 @@ int doList(Bundle* bundle) printf("\nNo resource table found.\n"); } else { printf("\nResource table:\n"); - res.print(); + res.print(false); } Asset* manifestAsset = assets.openNonAsset("AndroidManifest.xml", @@ -382,7 +382,7 @@ int doDump(Bundle* bundle) } if (strcmp("resources", option) == 0) { - res.print(); + res.print(bundle->getValues()); } else if (strcmp("xmltree", option) == 0) { if (bundle->getFileSpecCount() < 3) { @@ -734,11 +734,12 @@ int doDump(Bundle* bundle) activityIcon.string()); } } + printf("locales:"); Vector<String8> locales; res.getLocales(&locales); - const size_t N = locales.size(); - for (size_t i=0; i<N; i++) { + const size_t NL = locales.size(); + for (size_t i=0; i<NL; i++) { const char* localeStr = locales[i].string(); if (localeStr == NULL || strlen(localeStr) == 0) { localeStr = "--_--"; @@ -746,6 +747,24 @@ int doDump(Bundle* bundle) printf(" '%s'", localeStr); } printf("\n"); + + Vector<ResTable_config> configs; + res.getConfigurations(&configs); + SortedVector<int> densities; + const size_t NC = configs.size(); + for (size_t i=0; i<NC; i++) { + int dens = configs[i].density; + if (dens == 0) dens = 160; + densities.add(dens); + } + + printf("densities:"); + const size_t ND = densities.size(); + for (size_t i=0; i<ND; i++) { + printf(" '%d'", densities[i]); + } + printf("\n"); + AssetDir* dir = assets.openNonAssetDir(assetsCookie, "lib"); if (dir != NULL) { if (dir->getFileCount() > 0) { diff --git a/tools/aapt/Main.cpp b/tools/aapt/Main.cpp index a33b4d7..882714c 100644 --- a/tools/aapt/Main.cpp +++ b/tools/aapt/Main.cpp @@ -47,7 +47,7 @@ void usage(void) " %s l[ist] [-v] [-a] file.{zip,jar,apk}\n" " List contents of Zip-compatible archive.\n\n", gProgName); fprintf(stderr, - " %s d[ump] WHAT file.{apk} [asset [asset ...]]\n" + " %s d[ump] [--values] WHAT file.{apk} [asset [asset ...]]\n" " badging Print the label and icon for the app declared in APK.\n" " permissions Print the permissions from the APK.\n" " resources Print the resource table from the APK.\n" @@ -125,6 +125,8 @@ void usage(void) " inserts android:targetSdkVersion in to manifest.\n" " --max-sdk-version\n" " inserts android:maxSdkVersion in to manifest.\n" + " --values\n" + " when used with \"dump resources\" also includes resource values.\n" " --version-code\n" " inserts android:versionCode in to manifest.\n" " --version-name\n" @@ -398,6 +400,8 @@ int main(int argc, char* const argv[]) goto bail; } bundle.setVersionName(argv[0]); + } else if (strcmp(cp, "-values") == 0) { + bundle.setValues(true); } else { fprintf(stderr, "ERROR: Unknown option '-%s'\n", cp); wantUsage = true; diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java index 47a7ec0..d0a1c46 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java @@ -55,6 +55,8 @@ import android.view.View.AttachInfo; import android.view.View.MeasureSpec; import android.view.WindowManager.LayoutParams; import android.widget.FrameLayout; +import android.widget.TabHost; +import android.widget.TabWidget; import java.lang.ref.SoftReference; import java.lang.reflect.Field; @@ -69,10 +71,10 @@ import java.util.Map; * {@link #computeLayout(IXmlPullParser, Object, int, int, String, boolean, Map, Map, IProjectCallback, ILayoutLog)}. */ public final class Bridge implements ILayoutBridge { - + private static final int DEFAULT_TITLE_BAR_HEIGHT = 25; private static final int DEFAULT_STATUS_BAR_HEIGHT = 25; - + public static class StaticMethodNotImplementedException extends RuntimeException { private static final long serialVersionUID = 1L; @@ -82,19 +84,20 @@ public final class Bridge implements ILayoutBridge { } /** - * Maps from id to resource name/type. + * Maps from id to resource name/type. This is for android.R only. */ private final static Map<Integer, String[]> sRMap = new HashMap<Integer, String[]>(); /** - * Same as sRMap except for int[] instead of int resources. + * Same as sRMap except for int[] instead of int resources. This is for android.R only. */ private final static Map<int[], String> sRArrayMap = new HashMap<int[], String>(); /** - * Reverse map compared to sRMap, resource type -> (resource name -> id) + * Reverse map compared to sRMap, resource type -> (resource name -> id). + * This is for android.R only. */ private final static Map<String, Map<String, Integer>> sRFullMap = new HashMap<String, Map<String,Integer>>(); - + private final static Map<Object, Map<String, SoftReference<Bitmap>>> sProjectBitmapCache = new HashMap<Object, Map<String, SoftReference<Bitmap>>>(); private final static Map<Object, Map<String, SoftReference<NinePatch>>> sProject9PatchCache = @@ -104,7 +107,7 @@ public final class Bridge implements ILayoutBridge { new HashMap<String, SoftReference<Bitmap>>(); private final static Map<String, SoftReference<NinePatch>> sFramework9PatchCache = new HashMap<String, SoftReference<NinePatch>>(); - + private static Map<String, Map<String, Integer>> sEnumValueMap; /** @@ -156,14 +159,14 @@ public final class Bridge implements ILayoutBridge { return sinit(fontOsLocation, enumValueMap); } - + private static synchronized boolean sinit(String fontOsLocation, Map<String, Map<String, Integer>> enumValueMap) { // When DEBUG_LAYOUT is set and is not 0 or false, setup a default listener // on static (native) methods which prints the signature on the console and // throws an exception. - // This is useful when testing the rendering in ADT to identify static native + // This is useful when testing the rendering in ADT to identify static native // methods that are ignored -- layoutlib_create makes them returns 0/false/null // which is generally OK yet might be a problem, so this is how you'd find out. // @@ -214,7 +217,7 @@ public final class Bridge implements ILayoutBridge { } else { return false; } - + sEnumValueMap = enumValueMap; // now parse com.android.internal.R (and only this one as android.R is a subset of @@ -226,13 +229,13 @@ public final class Bridge implements ILayoutBridge { // int[] does not implement equals/hashCode, and if the parsing used a different class // loader for the R class, this would NOT work. Class<?> r = com.android.internal.R.class; - + for (Class<?> inner : r.getDeclaredClasses()) { String resType = inner.getSimpleName(); Map<String, Integer> fullMap = new HashMap<String, Integer>(); sRFullMap.put(resType, fullMap); - + for (Field f : inner.getDeclaredFields()) { // only process static final fields. Since the final attribute may have // been altered by layoutlib_create, we only check static @@ -243,7 +246,7 @@ public final class Bridge implements ILayoutBridge { // if the object is an int[] we put it in sRArrayMap sRArrayMap.put((int[]) f.get(null), f.getName()); } else if (type == int.class) { - Integer value = (Integer) f.get(null); + Integer value = (Integer) f.get(null); sRMap.put(value, new String[] { f.getName(), resType }); fullMap.put(f.getName(), value); } else { @@ -281,7 +284,7 @@ public final class Bridge implements ILayoutBridge { themeName = themeName.substring(1); isProjectTheme = true; } - + return computeLayout(layoutDescription, projectKey, screenWidth, screenHeight, DisplayMetrics.DEFAULT_DENSITY, DisplayMetrics.DEFAULT_DENSITY, DisplayMetrics.DEFAULT_DENSITY, @@ -294,6 +297,7 @@ public final class Bridge implements ILayoutBridge { * (non-Javadoc) * @see com.android.layoutlib.api.ILayoutBridge#computeLayout(com.android.layoutlib.api.IXmlPullParser, java.lang.Object, int, int, java.lang.String, boolean, java.util.Map, java.util.Map, com.android.layoutlib.api.IProjectCallback, com.android.layoutlib.api.ILayoutLog) */ + @Deprecated public ILayoutResult computeLayout(IXmlPullParser layoutDescription, Object projectKey, int screenWidth, int screenHeight, String themeName, boolean isProjectTheme, Map<String, Map<String, IResourceValue>> projectResources, @@ -319,7 +323,7 @@ public final class Bridge implements ILayoutBridge { if (logger == null) { logger = sDefaultLogger; } - + synchronized (sDefaultLogger) { sLogger = logger; } @@ -327,12 +331,12 @@ public final class Bridge implements ILayoutBridge { // find the current theme and compute the style inheritance map Map<IStyleResourceValue, IStyleResourceValue> styleParentMap = new HashMap<IStyleResourceValue, IStyleResourceValue>(); - + IStyleResourceValue currentTheme = computeStyleMaps(themeName, isProjectTheme, projectResources.get(BridgeConstants.RES_STYLE), frameworkResources.get(BridgeConstants.RES_STYLE), styleParentMap); - - BridgeContext context = null; + + BridgeContext context = null; try { // setup the display Metrics. DisplayMetrics metrics = new DisplayMetrics(); @@ -347,29 +351,32 @@ public final class Bridge implements ILayoutBridge { frameworkResources, styleParentMap, customViewLoader, logger); BridgeInflater inflater = new BridgeInflater(context, customViewLoader); context.setBridgeInflater(inflater); - + IResourceValue windowBackground = null; int screenOffset = 0; if (currentTheme != null) { windowBackground = context.findItemInStyle(currentTheme, "windowBackground"); windowBackground = context.resolveResValue(windowBackground); - + screenOffset = getScreenOffset(currentTheme, context); } - + // we need to make sure the Looper has been initialized for this thread. // this is required for View that creates Handler objects. if (Looper.myLooper() == null) { Looper.prepare(); } - + BridgeXmlBlockParser parser = new BridgeXmlBlockParser(layoutDescription, context, false /* platformResourceFlag */); - + ViewGroup root = new FrameLayout(context); - + View view = inflater.inflate(parser, root); - + + // post-inflate process. For now this supports TabHost/TabWidget + postInflateProcess(view, customViewLoader); + // set the AttachInfo on the root view. AttachInfo info = new AttachInfo(new WindowSession(), new Window(), new Handler(), null); @@ -392,16 +399,19 @@ public final class Bridge implements ILayoutBridge { // measure the views view.measure(w_spec, h_spec); view.layout(0, screenOffset, screenWidth, screenHeight); - + // draw them BridgeCanvas canvas = new BridgeCanvas(screenWidth, screenHeight - screenOffset, logger); - + root.draw(canvas); canvas.dispose(); - + return new LayoutResult(visit(((ViewGroup)view).getChildAt(0), context), canvas.getImage()); + } catch (PostInflateException e) { + return new LayoutResult(ILayoutResult.ERROR, "Error during post inflation process:\n" + + e.getMessage()); } catch (Throwable e) { // get the real cause of the exception. Throwable t = e; @@ -419,7 +429,7 @@ public final class Bridge implements ILayoutBridge { // Make sure to remove static references, otherwise we could not unload the lib BridgeResources.clearSystem(); BridgeAssetManager.clearSystem(); - + // Remove the global logger synchronized (sDefaultLogger) { sLogger = sDefaultLogger; @@ -437,18 +447,18 @@ public final class Bridge implements ILayoutBridge { sProject9PatchCache.remove(projectKey); } } - + /** * Returns details of a framework resource from its integer value. * @param value the integer value * @return an array of 2 strings containing the resource name and type, or null if the id - * does not match any resource. + * does not match any resource. */ public static String[] resolveResourceValue(int value) { return sRMap.get(value); - + } - + /** * Returns the name of a framework resource whose value is an int array. * @param array @@ -456,7 +466,7 @@ public final class Bridge implements ILayoutBridge { public static String resolveResourceValue(int[] array) { return sRArrayMap.get(array); } - + /** * Returns the integer id of a framework resource, from a given resource type and resource name. * @param type the type of the resource @@ -468,15 +478,15 @@ public final class Bridge implements ILayoutBridge { if (map != null) { return map.get(name); } - + return null; } - + static Map<String, Integer> getEnumValues(String attributeName) { if (sEnumValueMap != null) { return sEnumValueMap.get(attributeName); } - + return null; } @@ -507,13 +517,13 @@ public final class Bridge implements ILayoutBridge { return result; } - + /** * Compute style information from the given list of style for the project and framework. * @param themeName the name of the current theme. In order to differentiate project and * platform themes sharing the same name, all project themes must be prepended with * a '*' character. - * @param isProjectTheme Is this a project theme + * @param isProjectTheme Is this a project theme * @param inProjectStyleMap the project style map * @param inFrameworkStyleMap the framework style map * @param outInheritanceMap the map of style inheritance. This is filled by the method @@ -523,23 +533,23 @@ public final class Bridge implements ILayoutBridge { String themeName, boolean isProjectTheme, Map<String, IResourceValue> inProjectStyleMap, Map<String, IResourceValue> inFrameworkStyleMap, Map<IStyleResourceValue, IStyleResourceValue> outInheritanceMap) { - + if (inProjectStyleMap != null && inFrameworkStyleMap != null) { // first, get the theme IResourceValue theme = null; - + // project theme names have been prepended with a * if (isProjectTheme) { theme = inProjectStyleMap.get(themeName); } else { theme = inFrameworkStyleMap.get(themeName); } - + if (theme instanceof IStyleResourceValue) { // compute the inheritance map for both the project and framework styles computeStyleInheritance(inProjectStyleMap.values(), inProjectStyleMap, inFrameworkStyleMap, outInheritanceMap); - + // Compute the style inheritance for the framework styles/themes. // Since, for those, the style parent values do not contain 'android:' // we want to force looking in the framework style only to avoid using @@ -547,11 +557,11 @@ public final class Bridge implements ILayoutBridge { // To do this, we pass null in lieu of the project style map. computeStyleInheritance(inFrameworkStyleMap.values(), null /*inProjectStyleMap */, inFrameworkStyleMap, outInheritanceMap); - + return (IStyleResourceValue)theme; } } - + return null; } @@ -573,7 +583,7 @@ public final class Bridge implements ILayoutBridge { // first look for a specified parent. String parentName = style.getParentStyle(); - + // no specified parent? try to infer it from the name of the style. if (parentName == null) { parentName = getParentName(value.getName()); @@ -581,7 +591,7 @@ public final class Bridge implements ILayoutBridge { if (parentName != null) { parentStyle = getStyle(parentName, inProjectStyleMap, inFrameworkStyleMap); - + if (parentStyle != null) { outInheritanceMap.put(style, parentStyle); } @@ -589,7 +599,7 @@ public final class Bridge implements ILayoutBridge { } } } - + /** * Searches for and returns the {@link IStyleResourceValue} from a given name. * <p/>The format of the name can be: @@ -607,27 +617,27 @@ public final class Bridge implements ILayoutBridge { Map<String, IResourceValue> inProjectStyleMap, Map<String, IResourceValue> inFrameworkStyleMap) { boolean frameworkOnly = false; - + String name = parentName; - + // remove the useless @ if it's there if (name.startsWith(BridgeConstants.PREFIX_RESOURCE_REF)) { name = name.substring(BridgeConstants.PREFIX_RESOURCE_REF.length()); } - + // check for framework identifier. if (name.startsWith(BridgeConstants.PREFIX_ANDROID)) { frameworkOnly = true; name = name.substring(BridgeConstants.PREFIX_ANDROID.length()); } - + // at this point we could have the format style/<name>. we want only the name if (name.startsWith(BridgeConstants.REFERENCE_STYLE)) { name = name.substring(BridgeConstants.REFERENCE_STYLE.length()); } IResourceValue parent = null; - + // if allowed, search in the project resources. if (frameworkOnly == false && inProjectStyleMap != null) { parent = inProjectStyleMap.get(name); @@ -637,17 +647,17 @@ public final class Bridge implements ILayoutBridge { if (parent == null) { parent = inFrameworkStyleMap.get(name); } - + // make sure the result is the proper class type and return it. if (parent instanceof IStyleResourceValue) { return (IStyleResourceValue)parent; } - + sLogger.error(String.format("Unable to resolve parent style name: ", parentName)); - + return null; } - + /** * Computes the name of the parent style, or <code>null</code> if the style is a root style. */ @@ -656,10 +666,10 @@ public final class Bridge implements ILayoutBridge { if (index != -1) { return styleName.substring(0, index); } - + return null; } - + /** * Returns the top screen offset. This depends on whether the current theme defines the user * of the title and status bars. @@ -670,7 +680,7 @@ public final class Bridge implements ILayoutBridge { // get the title bar flag from the current theme. IResourceValue value = context.findItemInStyle(currentTheme, "windowNoTitle"); - + // because it may reference something else, we resolve it. value = context.resolveResValue(value); @@ -679,10 +689,10 @@ public final class Bridge implements ILayoutBridge { XmlUtils.convertValueToBoolean(value.getValue(), false /* defValue */) == false) { // get value from the theme. value = context.findItemInStyle(currentTheme, "windowTitleSize"); - + // resolve it value = context.resolveResValue(value); - + // default value offset = DEFAULT_TITLE_BAR_HEIGHT; @@ -690,17 +700,17 @@ public final class Bridge implements ILayoutBridge { if (value != null) { TypedValue typedValue = ResourceHelper.getValue(value.getValue()); if (typedValue != null) { - offset = (int)typedValue.getDimension(context.getResources().mMetrics); + offset = (int)typedValue.getDimension(context.getResources().mMetrics); } } } - + // get the fullscreen flag from the current theme. value = context.findItemInStyle(currentTheme, "windowFullscreen"); - + // because it may reference something else, we resolve it. value = context.resolveResValue(value); - + if (value == null || value.getValue() == null || XmlUtils.convertValueToBoolean(value.getValue(), false /* defValue */) == false) { // FIXME: Right now this is hard-coded in the platform, but once there's a constant, we'll need to use it. @@ -711,6 +721,94 @@ public final class Bridge implements ILayoutBridge { } /** + * Post process on a view hierachy that was just inflated. + * <p/>At the moment this only support TabHost: If {@link TabHost} is detected, look for the + * {@link TabWidget}, and the corresponding {@link FrameLayout} and make new tabs automatically + * based on the content of the {@link FrameLayout}. + * @param view the root view to process. + * @param projectCallback callback to the project. + */ + private void postInflateProcess(View view, IProjectCallback projectCallback) + throws PostInflateException { + if (view instanceof TabHost) { + setupTabHost((TabHost)view, projectCallback); + } else if (view instanceof ViewGroup) { + ViewGroup group = (ViewGroup)view; + final int count = group.getChildCount(); + for (int c = 0 ; c < count ; c++) { + View child = group.getChildAt(c); + postInflateProcess(child, projectCallback); + } + } + } + + /** + * Sets up a {@link TabHost} object. + * @param tabHost the TabHost to setup. + * @param projectCallback The project callback object to access the project R class. + * @throws PostInflateException + */ + private void setupTabHost(TabHost tabHost, IProjectCallback projectCallback) + throws PostInflateException { + // look for the TabWidget, and the FrameLayout. They have their own specific names + View v = tabHost.findViewById(android.R.id.tabs); + + if (v == null) { + throw new PostInflateException( + "TabHost requires a TabWidget with id \"android:id/tabs\".\n"); + } + + if ((v instanceof TabWidget) == false) { + throw new PostInflateException(String.format( + "TabHost requires a TabWidget with id \"android:id/tabs\".\n" + + "View found with id 'tabs' is '%s'", v.getClass().getCanonicalName())); + } + + v = tabHost.findViewById(android.R.id.tabcontent); + + if (v == null) { + // TODO: see if we can fake tabs even without the FrameLayout (same below when the framelayout is empty) + throw new PostInflateException( + "TabHost requires a FrameLayout with id \"android:id/tabcontent\"."); + } + + if ((v instanceof FrameLayout) == false) { + throw new PostInflateException(String.format( + "TabHost requires a FrameLayout with id \"android:id/tabcontent\".\n" + + "View found with id 'tabcontent' is '%s'", v.getClass().getCanonicalName())); + } + + FrameLayout content = (FrameLayout)v; + + // now process the content of the framelayout and dynamically create tabs for it. + final int count = content.getChildCount(); + + if (count == 0) { + throw new PostInflateException( + "The FrameLayout for the TabHost has no content. Rendering failed.\n"); + } + + // this must be called before addTab() so that the TabHost searches its TabWidget + // and FrameLayout. + tabHost.setup(); + + // for each child of the framelayout, add a new TabSpec + for (int i = 0 ; i < count ; i++) { + View child = content.getChildAt(i); + String tabSpec = String.format("tab_spec%d", i+1); + int id = child.getId(); + String[] resource = projectCallback.resolveResourceValue(id); + String name; + if (resource != null) { + name = resource[0]; // 0 is resource name, 1 is resource type. + } else { + name = String.format("Tab %d", i+1); // default name if id is unresolved. + } + tabHost.addTab(tabHost.newTabSpec(tabSpec).setIndicator(name).setContent(id)); + } + } + + /** * Returns the bitmap for a specific path, from a specific project cache, or from the * framework cache. * @param value the path of the bitmap @@ -750,7 +848,7 @@ public final class Bridge implements ILayoutBridge { map = new HashMap<String, SoftReference<Bitmap>>(); sProjectBitmapCache.put(projectKey, map); } - + map.put(value, new SoftReference<Bitmap>(bmp)); } else { sFrameworkBitmapCache.put(value, new SoftReference<Bitmap>(bmp)); @@ -767,7 +865,7 @@ public final class Bridge implements ILayoutBridge { static NinePatch getCached9Patch(String value, Object projectKey) { if (projectKey != null) { Map<String, SoftReference<NinePatch>> map = sProject9PatchCache.get(projectKey); - + if (map != null) { SoftReference<NinePatch> ref = map.get(value); if (ref != null) { @@ -780,7 +878,7 @@ public final class Bridge implements ILayoutBridge { return ref.get(); } } - + return null; } @@ -798,13 +896,21 @@ public final class Bridge implements ILayoutBridge { map = new HashMap<String, SoftReference<NinePatch>>(); sProject9PatchCache.put(projectKey, map); } - + map.put(value, new SoftReference<NinePatch>(ninePatch)); } else { sFramework9PatchCache.put(value, new SoftReference<NinePatch>(ninePatch)); } } + private static final class PostInflateException extends Exception { + private static final long serialVersionUID = 1L; + + public PostInflateException(String message) { + super(message); + } + } + /** * Implementation of {@link IWindowSession} so that mSession is not null in * the {@link SurfaceView}. @@ -839,7 +945,7 @@ public final class Bridge implements ILayoutBridge { // pass for now. return false; } - + @SuppressWarnings("unused") public MotionEvent getPendingPointerMove(IWindow arg0) throws RemoteException { // pass for now. @@ -863,7 +969,7 @@ public final class Bridge implements ILayoutBridge { public void getDisplayFrame(IWindow window, Rect outDisplayFrame) { // pass for now. } - + @SuppressWarnings("unused") public void remove(IWindow arg0) throws RemoteException { // pass for now. @@ -883,13 +989,13 @@ public final class Bridge implements ILayoutBridge { Rect visibleInsets) { // pass for now. } - + public IBinder asBinder() { // pass for now. return null; } } - + /** * Implementation of {@link IWindow} to pass to the {@link AttachInfo}. */ diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContext.java index d0896b5..69f3d9c 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContext.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContext.java @@ -29,6 +29,7 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.ServiceConnection; import android.content.SharedPreferences; +import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.res.AssetManager; import android.content.res.Configuration; @@ -960,6 +961,12 @@ public final class BridgeContext extends Context { } @Override + public ApplicationInfo getApplicationInfo() { + // TODO Auto-generated method stub + return null; + } + + @Override public String getPackageResourcePath() { // TODO Auto-generated method stub return null; |