diff options
151 files changed, 6863 insertions, 3637 deletions
diff --git a/api/current.xml b/api/current.xml index 5e57d80..42787e6 100644 --- a/api/current.xml +++ b/api/current.xml @@ -947,6 +947,17 @@ visibility="public" > </field> +<field name="SET_ALARM" + type="java.lang.String" + transient="false" + volatile="false" + value=""com.android.alarm.permission.SET_ALARM"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="SET_ALWAYS_FINISH" type="java.lang.String" transient="false" @@ -21741,6 +21752,17 @@ visibility="public" > </method> +<method name="removeAllUpdateListeners" + return="void" + abstract="false" + native="false" + synchronized="false" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</method> <method name="removeUpdateListener" return="void" abstract="false" @@ -105938,6 +105960,17 @@ visibility="public" > </field> +<field name="ACTION_VIEW_DOWNLOADS" + type="java.lang.String" + transient="false" + volatile="false" + value=""android.intent.action.VIEW_DOWNLOADS"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> <field name="COLUMN_BYTES_DOWNLOADED_SO_FAR" type="java.lang.String" transient="false" @@ -143307,6 +143340,67 @@ </package> <package name="android.provider" > +<class name="AlarmClock" + extends="java.lang.Object" + abstract="false" + static="false" + final="true" + deprecated="not deprecated" + visibility="public" +> +<constructor name="AlarmClock" + type="android.provider.AlarmClock" + static="false" + final="false" + deprecated="not deprecated" + visibility="public" +> +</constructor> +<field name="ACTION_SET_ALARM" + type="java.lang.String" + transient="false" + volatile="false" + value=""android.intent.action.SET_ALARM"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="EXTRA_HOUR" + type="java.lang.String" + transient="false" + volatile="false" + value=""android.intent.extra.alarm.HOUR"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="EXTRA_MESSAGE" + type="java.lang.String" + transient="false" + volatile="false" + value=""android.intent.extra.alarm.MESSAGE"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +<field name="EXTRA_MINUTES" + type="java.lang.String" + transient="false" + volatile="false" + value=""android.intent.extra.alarm.MINUTES"" + static="true" + final="true" + deprecated="not deprecated" + visibility="public" +> +</field> +</class> <interface name="BaseColumns" abstract="true" static="false" diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java index f901bfb..b073004 100644 --- a/cmds/am/src/com/android/commands/am/Am.java +++ b/cmds/am/src/com/android/commands/am/Am.java @@ -19,6 +19,7 @@ package com.android.commands.am; import android.app.ActivityManagerNative; +import android.app.IActivityController; import android.app.IActivityManager; import android.app.IInstrumentationWatcher; import android.app.Instrumentation; @@ -28,12 +29,16 @@ import android.content.Intent; import android.net.Uri; import android.os.Bundle; import android.os.ParcelFileDescriptor; +import android.os.RemoteException; import android.os.ServiceManager; import android.util.AndroidException; import android.view.IWindowManager; +import java.io.BufferedReader; import java.io.File; import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStreamReader; import java.io.PrintStream; import java.net.URISyntaxException; import java.util.Iterator; @@ -99,6 +104,8 @@ public class Am { runProfile(); } else if (op.equals("dumpheap")) { runDumpHeap(); + } else if (op.equals("monitor")) { + runMonitor(); } else { throw new IllegalArgumentException("Unknown command: " + op); } @@ -472,6 +479,303 @@ public class Am { } } + class MyActivityController extends IActivityController.Stub { + final String mGdbPort; + + static final int STATE_NORMAL = 0; + static final int STATE_CRASHED = 1; + static final int STATE_EARLY_ANR = 2; + static final int STATE_ANR = 3; + + int mState; + + static final int RESULT_DEFAULT = 0; + + static final int RESULT_CRASH_DIALOG = 0; + static final int RESULT_CRASH_KILL = 1; + + static final int RESULT_EARLY_ANR_CONTINUE = 0; + static final int RESULT_EARLY_ANR_KILL = 1; + + static final int RESULT_ANR_DIALOG = 0; + static final int RESULT_ANR_KILL = 1; + static final int RESULT_ANR_WAIT = 1; + + int mResult; + + Process mGdbProcess; + Thread mGdbThread; + boolean mGotGdbPrint; + + MyActivityController(String gdbPort) { + mGdbPort = gdbPort; + } + + @Override + public boolean activityResuming(String pkg) throws RemoteException { + synchronized (this) { + System.out.println("** Activity resuming: " + pkg); + } + return true; + } + + @Override + public boolean activityStarting(Intent intent, String pkg) throws RemoteException { + synchronized (this) { + System.out.println("** Activity starting: " + pkg); + } + return true; + } + + @Override + public boolean appCrashed(String processName, int pid, String shortMsg, String longMsg, + long timeMillis, String stackTrace) throws RemoteException { + synchronized (this) { + System.out.println("** ERROR: PROCESS CRASHED"); + System.out.println("processName: " + processName); + System.out.println("processPid: " + pid); + System.out.println("shortMsg: " + shortMsg); + System.out.println("longMsg: " + longMsg); + System.out.println("timeMillis: " + timeMillis); + System.out.println("stack:"); + System.out.print(stackTrace); + System.out.println("#"); + int result = waitControllerLocked(pid, STATE_CRASHED); + return result == RESULT_CRASH_KILL ? false : true; + } + } + + @Override + public int appEarlyNotResponding(String processName, int pid, String annotation) + throws RemoteException { + synchronized (this) { + System.out.println("** ERROR: EARLY PROCESS NOT RESPONDING"); + System.out.println("processName: " + processName); + System.out.println("processPid: " + pid); + System.out.println("annotation: " + annotation); + int result = waitControllerLocked(pid, STATE_EARLY_ANR); + if (result == RESULT_EARLY_ANR_KILL) return -1; + return 0; + } + } + + @Override + public int appNotResponding(String processName, int pid, String processStats) + throws RemoteException { + synchronized (this) { + System.out.println("** ERROR: PROCESS NOT RESPONDING"); + System.out.println("processName: " + processName); + System.out.println("processPid: " + pid); + System.out.println("processStats:"); + System.out.print(processStats); + System.out.println("#"); + int result = waitControllerLocked(pid, STATE_ANR); + if (result == RESULT_ANR_KILL) return -1; + if (result == RESULT_ANR_WAIT) return 1; + return 0; + } + } + + void killGdbLocked() { + mGotGdbPrint = false; + if (mGdbProcess != null) { + System.out.println("Stopping gdbserver"); + mGdbProcess.destroy(); + mGdbProcess = null; + } + if (mGdbThread != null) { + mGdbThread.interrupt(); + mGdbThread = null; + } + } + + int waitControllerLocked(int pid, int state) { + if (mGdbPort != null) { + killGdbLocked(); + + try { + System.out.println("Starting gdbserver on port " + mGdbPort); + System.out.println("Do the following:"); + System.out.println(" adb forward tcp:" + mGdbPort + " tcp:" + mGdbPort); + System.out.println(" gdbclient app_process :" + mGdbPort); + + mGdbProcess = Runtime.getRuntime().exec(new String[] { + "gdbserver", ":" + mGdbPort, "--attach", Integer.toString(pid) + }); + final InputStreamReader converter = new InputStreamReader( + mGdbProcess.getInputStream()); + mGdbThread = new Thread() { + @Override + public void run() { + BufferedReader in = new BufferedReader(converter); + String line; + int count = 0; + while (true) { + synchronized (MyActivityController.this) { + if (mGdbThread == null) { + return; + } + if (count == 2) { + mGotGdbPrint = true; + MyActivityController.this.notifyAll(); + } + } + try { + line = in.readLine(); + if (line == null) { + return; + } + System.out.println("GDB: " + line); + count++; + } catch (IOException e) { + return; + } + } + } + }; + mGdbThread.start(); + + // Stupid waiting for .5s. Doesn't matter if we end early. + try { + this.wait(500); + } catch (InterruptedException e) { + } + + } catch (IOException e) { + System.err.println("Failure starting gdbserver: " + e); + killGdbLocked(); + } + } + mState = state; + System.out.println(""); + printMessageForState(); + + while (mState != STATE_NORMAL) { + try { + wait(); + } catch (InterruptedException e) { + } + } + + killGdbLocked(); + + return mResult; + } + + void resumeController(int result) { + synchronized (this) { + mState = STATE_NORMAL; + mResult = result; + notifyAll(); + } + } + + void printMessageForState() { + switch (mState) { + case STATE_NORMAL: + System.out.println("Monitoring activity manager... available commands:"); + break; + case STATE_CRASHED: + System.out.println("Waiting after crash... available commands:"); + System.out.println("(c)ontinue: show crash dialog"); + System.out.println("(k)ill: immediately kill app"); + break; + case STATE_EARLY_ANR: + System.out.println("Waiting after early ANR... available commands:"); + System.out.println("(c)ontinue: standard ANR processing"); + System.out.println("(k)ill: immediately kill app"); + break; + case STATE_ANR: + System.out.println("Waiting after ANR... available commands:"); + System.out.println("(c)ontinue: show ANR dialog"); + System.out.println("(k)ill: immediately kill app"); + System.out.println("(w)ait: wait some more"); + break; + } + System.out.println("(q)uit: finish monitoring"); + } + + void run() throws RemoteException { + try { + printMessageForState(); + + mAm.setActivityController(this); + mState = STATE_NORMAL; + + InputStreamReader converter = new InputStreamReader(System.in); + BufferedReader in = new BufferedReader(converter); + String line; + + while ((line = in.readLine()) != null) { + boolean addNewline = true; + if (line.length() <= 0) { + addNewline = false; + } else if ("q".equals(line) || "quit".equals(line)) { + resumeController(RESULT_DEFAULT); + break; + } else if (mState == STATE_CRASHED) { + if ("c".equals(line) || "continue".equals(line)) { + resumeController(RESULT_CRASH_DIALOG); + } else if ("k".equals(line) || "kill".equals(line)) { + resumeController(RESULT_CRASH_KILL); + } else { + System.out.println("Invalid command: " + line); + } + } else if (mState == STATE_ANR) { + if ("c".equals(line) || "continue".equals(line)) { + resumeController(RESULT_ANR_DIALOG); + } else if ("k".equals(line) || "kill".equals(line)) { + resumeController(RESULT_ANR_KILL); + } else if ("w".equals(line) || "wait".equals(line)) { + resumeController(RESULT_ANR_WAIT); + } else { + System.out.println("Invalid command: " + line); + } + } else if (mState == STATE_EARLY_ANR) { + if ("c".equals(line) || "continue".equals(line)) { + resumeController(RESULT_EARLY_ANR_CONTINUE); + } else if ("k".equals(line) || "kill".equals(line)) { + resumeController(RESULT_EARLY_ANR_KILL); + } else { + System.out.println("Invalid command: " + line); + } + } else { + System.out.println("Invalid command: " + line); + } + + synchronized (this) { + if (addNewline) { + System.out.println(""); + } + printMessageForState(); + } + } + + } catch (IOException e) { + e.printStackTrace(); + } finally { + mAm.setActivityController(null); + } + } + } + + private void runMonitor() throws Exception { + String opt; + String gdbPort = null; + while ((opt=nextOption()) != null) { + if (opt.equals("--gdb")) { + gdbPort = nextArgRequired(); + } else { + System.err.println("Error: Unknown option: " + opt); + showUsage(); + return; + } + } + + MyActivityController controller = new MyActivityController(gdbPort); + controller.run(); + } + private class IntentReceiver extends IIntentReceiver.Stub { private boolean mFinished = false; @@ -649,6 +953,9 @@ public class Am { " dump heap: am dumpheap [flags] <PROCESS> <FILE>\n" + " -n: dump native heap instead of managed heap\n" + "\n" + + " start monitoring: am monitor [--gdb <port>]\n" + + " --gdb: start gdbserv on the given port at crash/ANR\n" + + "\n" + " <INTENT> specifications include these flags:\n" + " [-a <ACTION>] [-d <DATA_URI>] [-t <MIME_TYPE>]\n" + " [-c <CATEGORY> [-c <CATEGORY>] ...]\n" + diff --git a/cmds/dumpstate/dumpstate.c b/cmds/dumpstate/dumpstate.c index a09666e..67bd9f7 100644 --- a/cmds/dumpstate/dumpstate.c +++ b/cmds/dumpstate/dumpstate.c @@ -135,11 +135,11 @@ static void dumpstate() { run_command("PROCESSES AND THREADS", 10, "ps", "-t", "-p", "-P", NULL); run_command("LIBRANK", 10, "librank", NULL); - dump_file("BINDER FAILED TRANSACTION LOG", "/proc/binder/failed_transaction_log"); - dump_file("BINDER TRANSACTION LOG", "/proc/binder/transaction_log"); - dump_file("BINDER TRANSACTIONS", "/proc/binder/transactions"); - dump_file("BINDER STATS", "/proc/binder/stats"); - run_command("BINDER PROCESS STATE", 10, "sh", "-c", "cat /proc/binder/proc/*"); + dump_file("BINDER FAILED TRANSACTION LOG", "/sys/kernel/debug/binder/failed_transaction_log"); + dump_file("BINDER TRANSACTION LOG", "/sys/kernel/debug/binder/transaction_log"); + dump_file("BINDER TRANSACTIONS", "/sys/kernel/debug/binder/transactions"); + dump_file("BINDER STATS", "/sys/kernel/debug/binder/stats"); + dump_file("BINDER STATE", "/sys/kernel/debug/binder/state"); run_command("FILESYSTEMS & FREE SPACE", 10, "df", NULL); diff --git a/cmds/stagefright/stagefright.cpp b/cmds/stagefright/stagefright.cpp index 8ab94ad..8b54871 100644 --- a/cmds/stagefright/stagefright.cpp +++ b/cmds/stagefright/stagefright.cpp @@ -14,6 +14,10 @@ * limitations under the License. */ +//#define LOG_NDEBUG 0 +#define LOG_TAG "stagefright" +#include <media/stagefright/foundation/ADebug.h> + #include <sys/time.h> #include <stdlib.h> @@ -27,10 +31,11 @@ #include <media/IMediaPlayerService.h> #include <media/stagefright/foundation/ALooper.h> #include "include/ARTSPController.h" +#include "include/LiveSource.h" +#include "include/NuCachedSource2.h" #include <media/stagefright/AudioPlayer.h> #include <media/stagefright/DataSource.h> #include <media/stagefright/JPEGSource.h> -#include <media/stagefright/MediaDebug.h> #include <media/stagefright/MediaDefs.h> #include <media/stagefright/MediaErrors.h> #include <media/stagefright/MediaExtractor.h> @@ -43,6 +48,8 @@ #include <media/stagefright/foundation/hexdump.h> #include <media/stagefright/MPEG4Writer.h> +#include <fcntl.h> + using namespace android; static long gNumRepetitions; @@ -120,7 +127,7 @@ static void playSource(OMXClient *client, sp<MediaSource> &source) { bool shouldSeek = false; if (err == INFO_FORMAT_CHANGED) { - CHECK_EQ(buffer, NULL); + CHECK(buffer == NULL); printf("format changed.\n"); continue; @@ -206,7 +213,7 @@ static void playSource(OMXClient *client, sp<MediaSource> &source) { options.clearSeekTo(); if (err != OK) { - CHECK_EQ(buffer, NULL); + CHECK(buffer == NULL); if (err == INFO_FORMAT_CHANGED) { printf("format changed.\n"); @@ -267,14 +274,115 @@ static void playSource(OMXClient *client, sp<MediaSource> &source) { } } -static void writeSourceToMP4(const sp<MediaSource> &source) { +//////////////////////////////////////////////////////////////////////////////// + +struct DetectSyncSource : public MediaSource { + DetectSyncSource(const sp<MediaSource> &source); + + virtual status_t start(MetaData *params = NULL); + virtual status_t stop(); + virtual sp<MetaData> getFormat(); + + virtual status_t read( + MediaBuffer **buffer, const ReadOptions *options); + +private: + enum StreamType { + AVC, + MPEG4, + H263, + OTHER, + }; + + sp<MediaSource> mSource; + StreamType mStreamType; + + DISALLOW_EVIL_CONSTRUCTORS(DetectSyncSource); +}; + +DetectSyncSource::DetectSyncSource(const sp<MediaSource> &source) + : mSource(source), + mStreamType(OTHER) { + const char *mime; + CHECK(mSource->getFormat()->findCString(kKeyMIMEType, &mime)); + + if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC)) { + mStreamType = AVC; + } else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_MPEG4)) { + mStreamType = MPEG4; + CHECK(!"sync frame detection not implemented yet for MPEG4"); + } else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_H263)) { + mStreamType = H263; + CHECK(!"sync frame detection not implemented yet for H.263"); + } +} + +status_t DetectSyncSource::start(MetaData *params) { + return mSource->start(params); +} + +status_t DetectSyncSource::stop() { + return mSource->stop(); +} + +sp<MetaData> DetectSyncSource::getFormat() { + return mSource->getFormat(); +} + +static bool isIDRFrame(MediaBuffer *buffer) { + const uint8_t *data = + (const uint8_t *)buffer->data() + buffer->range_offset(); + size_t size = buffer->range_length(); + for (size_t i = 0; i + 3 < size; ++i) { + if (!memcmp("\x00\x00\x01", &data[i], 3)) { + uint8_t nalType = data[i + 3] & 0x1f; + if (nalType == 5) { + return true; + } + } + } + + return false; +} + +status_t DetectSyncSource::read( + MediaBuffer **buffer, const ReadOptions *options) { + status_t err = mSource->read(buffer, options); + + if (err != OK) { + return err; + } + + if (mStreamType == AVC && isIDRFrame(*buffer)) { + (*buffer)->meta_data()->setInt32(kKeyIsSyncFrame, true); + } else { + (*buffer)->meta_data()->setInt32(kKeyIsSyncFrame, true); + } + + return OK; +} + +//////////////////////////////////////////////////////////////////////////////// + +static void writeSourcesToMP4( + Vector<sp<MediaSource> > &sources, bool syncInfoPresent) { sp<MPEG4Writer> writer = new MPEG4Writer(gWriteMP4Filename.string()); - CHECK_EQ(writer->addSource(source), OK); + // at most one minute. + writer->setMaxFileDuration(60000000ll); + + for (size_t i = 0; i < sources.size(); ++i) { + sp<MediaSource> source = sources.editItemAt(i); + + CHECK_EQ(writer->addSource( + syncInfoPresent ? source : new DetectSyncSource(source)), + (status_t)OK); + } sp<MetaData> params = new MetaData; - CHECK_EQ(writer->start(), OK); + params->setInt32(kKeyNotRealTime, true); + CHECK_EQ(writer->start(params.get()), (status_t)OK); while (!writer->reachedEOS()) { usleep(100000); @@ -283,7 +391,7 @@ static void writeSourceToMP4(const sp<MediaSource> &source) { } static void performSeekTest(const sp<MediaSource> &source) { - CHECK_EQ(OK, source->start()); + CHECK_EQ((status_t)OK, source->start()); int64_t durationUs; CHECK(source->getFormat()->findInt64(kKeyDuration, &durationUs)); @@ -335,7 +443,7 @@ static void performSeekTest(const sp<MediaSource> &source) { } } - CHECK_EQ(OK, source->stop()); + CHECK_EQ((status_t)OK, source->stop()); } static void usage(const char *me) { @@ -481,10 +589,10 @@ int main(int argc, char **argv) { for (int k = 0; k < argc; ++k) { const char *filename = argv[k]; - CHECK_EQ(retriever->setDataSource(filename), OK); + CHECK_EQ(retriever->setDataSource(filename), (status_t)OK); CHECK_EQ(retriever->setMode( METADATA_MODE_FRAME_CAPTURE_AND_METADATA_RETRIEVAL), - OK); + (status_t)OK); sp<IMemory> mem = retriever->captureFrame(); @@ -530,7 +638,7 @@ int main(int argc, char **argv) { Vector<CodecCapabilities> results; CHECK_EQ(QueryCodecs(omx, kMimeTypes[k], true, // queryDecoders - &results), OK); + &results), (status_t)OK); for (size_t i = 0; i < results.size(); ++i) { printf(" decoder '%s' supports ", @@ -579,12 +687,16 @@ int main(int argc, char **argv) { status_t err = client.connect(); for (int k = 0; k < argc; ++k) { + bool syncInfoPresent = true; + const char *filename = argv[k]; sp<DataSource> dataSource = DataSource::CreateFromURI(filename); - if ((strncasecmp(filename, "sine:", 5) - && strncasecmp(filename, "rtsp://", 7)) && dataSource == NULL) { + if (strncasecmp(filename, "sine:", 5) + && strncasecmp(filename, "rtsp://", 7) + && strncasecmp(filename, "httplive://", 11) + && dataSource == NULL) { fprintf(stderr, "Unable to create data source.\n"); return 1; } @@ -596,10 +708,14 @@ int main(int argc, char **argv) { isJPEG = true; } + Vector<sp<MediaSource> > mediaSources; sp<MediaSource> mediaSource; if (isJPEG) { mediaSource = new JPEGSource(dataSource); + if (gWriteMP4) { + mediaSources.push(mediaSource); + } } else if (!strncasecmp("sine:", filename, 5)) { char *end; long sampleRate = strtol(filename + 5, &end, 10); @@ -608,6 +724,9 @@ int main(int argc, char **argv) { sampleRate = 44100; } mediaSource = new SineSource(sampleRate, 1); + if (gWriteMP4) { + mediaSources.push(mediaSource); + } } else { sp<MediaExtractor> extractor; @@ -625,6 +744,20 @@ int main(int argc, char **argv) { } extractor = rtspController.get(); + + syncInfoPresent = false; + } else if (!strncasecmp("httplive://", filename, 11)) { + String8 uri("http://"); + uri.append(filename + 11); + + dataSource = new LiveSource(uri.string()); + dataSource = new NuCachedSource2(dataSource); + + extractor = + MediaExtractor::Create( + dataSource, MEDIA_MIMETYPE_CONTAINER_MPEG2TS); + + syncInfoPresent = false; } else { extractor = MediaExtractor::Create(dataSource); if (extractor == NULL) { @@ -635,46 +768,75 @@ int main(int argc, char **argv) { size_t numTracks = extractor->countTracks(); - sp<MetaData> meta; - size_t i; - for (i = 0; i < numTracks; ++i) { - meta = extractor->getTrackMetaData( - i, MediaExtractor::kIncludeExtensiveMetaData); + if (gWriteMP4) { + bool haveAudio = false; + bool haveVideo = false; + for (size_t i = 0; i < numTracks; ++i) { + sp<MediaSource> source = extractor->getTrack(i); + + const char *mime; + CHECK(source->getFormat()->findCString( + kKeyMIMEType, &mime)); + + bool useTrack = false; + if (!haveAudio && !strncasecmp("audio/", mime, 6)) { + haveAudio = true; + useTrack = true; + } else if (!haveVideo && !strncasecmp("video/", mime, 6)) { + haveVideo = true; + useTrack = true; + } - const char *mime; - meta->findCString(kKeyMIMEType, &mime); + if (useTrack) { + mediaSources.push(source); - if (audioOnly && !strncasecmp(mime, "audio/", 6)) { - break; + if (haveAudio && haveVideo) { + break; + } + } } + } else { + sp<MetaData> meta; + size_t i; + for (i = 0; i < numTracks; ++i) { + meta = extractor->getTrackMetaData( + i, MediaExtractor::kIncludeExtensiveMetaData); - if (!audioOnly && !strncasecmp(mime, "video/", 6)) { - break; + const char *mime; + meta->findCString(kKeyMIMEType, &mime); + + if (audioOnly && !strncasecmp(mime, "audio/", 6)) { + break; + } + + if (!audioOnly && !strncasecmp(mime, "video/", 6)) { + break; + } + + meta = NULL; } - meta = NULL; - } + if (meta == NULL) { + fprintf(stderr, + "No suitable %s track found. The '-a' option will " + "target audio tracks only, the default is to target " + "video tracks only.\n", + audioOnly ? "audio" : "video"); + return -1; + } - if (meta == NULL) { - fprintf(stderr, - "No suitable %s track found. The '-a' option will " - "target audio tracks only, the default is to target " - "video tracks only.\n", - audioOnly ? "audio" : "video"); - return -1; - } + int64_t thumbTimeUs; + if (meta->findInt64(kKeyThumbnailTime, &thumbTimeUs)) { + printf("thumbnailTime: %lld us (%.2f secs)\n", + thumbTimeUs, thumbTimeUs / 1E6); + } - int64_t thumbTimeUs; - if (meta->findInt64(kKeyThumbnailTime, &thumbTimeUs)) { - printf("thumbnailTime: %lld us (%.2f secs)\n", - thumbTimeUs, thumbTimeUs / 1E6); + mediaSource = extractor->getTrack(i); } - - mediaSource = extractor->getTrack(i); } if (gWriteMP4) { - writeSourceToMP4(mediaSource); + writeSourcesToMP4(mediaSources, syncInfoPresent); } else if (seekTest) { performSeekTest(mediaSource); } else { diff --git a/core/java/android/animation/ValueAnimator.java b/core/java/android/animation/ValueAnimator.java index 1e2bbcc..02b2dce 100755 --- a/core/java/android/animation/ValueAnimator.java +++ b/core/java/android/animation/ValueAnimator.java @@ -614,6 +614,17 @@ public class ValueAnimator<T> extends Animator { } /** + * Removes all listeners from the set listening to frame updates for this animation. + */ + public void removeAllUpdateListeners() { + if (mUpdateListeners == null) { + return; + } + mUpdateListeners.clear(); + mUpdateListeners = null; + } + + /** * Removes a listener from the set listening to frame updates for this animation. * * @param listener the listener to be removed from the current set of update listeners @@ -685,7 +696,15 @@ public class ValueAnimator<T> extends Animator { */ private void start(boolean playBackwards) { mPlayingBackwards = playBackwards; - if ((mStartDelay == 0) && (Thread.currentThread() == Looper.getMainLooper().getThread())) { + Looper looper = Looper.getMainLooper(); + final boolean isUiThread; + if (looper != null) { + isUiThread = Thread.currentThread() == looper.getThread(); + } else { + // ignore check if we don't have a Looper (this isn't an Activity) + isUiThread = true; + } + if ((mStartDelay == 0) && isUiThread) { if (mListeners != null) { ArrayList<AnimatorListener> tmpListeners = (ArrayList<AnimatorListener>) mListeners.clone(); diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 3f74904..2ff88da 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -61,6 +61,7 @@ import android.util.Config; import android.util.DisplayMetrics; import android.util.EventLog; import android.util.Log; +import android.util.LogPrinter; import android.util.Slog; import android.view.Display; import android.view.HardwareRenderer; @@ -121,6 +122,7 @@ public final class ActivityThread { private static final android.graphics.Bitmap.Config THUMBNAIL_FORMAT = Bitmap.Config.RGB_565; private static final boolean DEBUG = false; static final boolean localLOGV = DEBUG ? Config.LOGD : Config.LOGV; + static final boolean DEBUG_MESSAGES = false; static final boolean DEBUG_BROADCAST = false; private static final boolean DEBUG_RESULTS = false; private static final boolean DEBUG_BACKUP = false; @@ -913,7 +915,7 @@ public final class ActivityThread { public static final int DUMP_HEAP = 135; public static final int DUMP_ACTIVITY = 136; String codeToString(int code) { - if (localLOGV) { + if (DEBUG_MESSAGES) { switch (code) { case LAUNCH_ACTIVITY: return "LAUNCH_ACTIVITY"; case PAUSE_ACTIVITY: return "PAUSE_ACTIVITY"; @@ -957,6 +959,7 @@ public final class ActivityThread { return "(unknown)"; } public void handleMessage(Message msg) { + if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + msg.what); switch (msg.what) { case LAUNCH_ACTIVITY: { ActivityClientRecord r = (ActivityClientRecord)msg.obj; @@ -1084,6 +1087,7 @@ public final class ActivityThread { handleDumpActivity((DumpComponentInfo)msg.obj); break; } + if (DEBUG_MESSAGES) Slog.v(TAG, "<<< done: " + msg.what); } void maybeSnapshot() { @@ -1550,7 +1554,7 @@ public final class ActivityThread { private final void queueOrSendMessage(int what, Object obj, int arg1, int arg2) { synchronized (this) { - if (localLOGV) Slog.v( + if (DEBUG_MESSAGES) Slog.v( TAG, "SCHEDULE " + what + " " + mH.codeToString(what) + ": " + arg1 + " / " + obj); Message msg = Message.obtain(); @@ -3699,6 +3703,11 @@ public final class ActivityThread { ActivityThread thread = new ActivityThread(); thread.attach(false); + if (false) { + Looper.myLooper().setMessageLogging(new + LogPrinter(Log.DEBUG, "ActivityThread")); + } + Looper.loop(); if (Process.supportsProcesses()) { diff --git a/core/java/android/app/IActivityController.aidl b/core/java/android/app/IActivityController.aidl index c76a517..aca8305 100644 --- a/core/java/android/app/IActivityController.aidl +++ b/core/java/android/app/IActivityController.aidl @@ -48,6 +48,11 @@ interface IActivityController long timeMillis, String stackTrace); /** + * Early call as soon as an ANR is detected. + */ + int appEarlyNotResponding(String processName, int pid, String annotation); + + /** * An application process is not responding. Return 0 to show the "app * not responding" dialog, 1 to continue waiting, or -1 to kill it * immediately. diff --git a/core/java/android/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java index 3df1790..4cb7026 100644 --- a/core/java/android/database/sqlite/SQLiteDatabase.java +++ b/core/java/android/database/sqlite/SQLiteDatabase.java @@ -2428,73 +2428,73 @@ public class SQLiteDatabase extends SQLiteClosable { */ /* package */ static ArrayList<DbStats> getDbStats() { ArrayList<DbStats> dbStatsList = new ArrayList<DbStats>(); - // make a local copy of mActiveDatabases - so that this method is not competing - // for synchronization lock on mActiveDatabases - ArrayList<WeakReference<SQLiteDatabase>> tempList; - synchronized(mActiveDatabases) { - tempList = (ArrayList<WeakReference<SQLiteDatabase>>)mActiveDatabases.clone(); - } - for (WeakReference<SQLiteDatabase> w : tempList) { - SQLiteDatabase db = w.get(); - if (db == null || !db.isOpen()) { - continue; - } - - synchronized (db) { - try { - // get SQLITE_DBSTATUS_LOOKASIDE_USED for the db - int lookasideUsed = db.native_getDbLookaside(); - - // get the lastnode of the dbname - String path = db.getPath(); - int indx = path.lastIndexOf("/"); - String lastnode = path.substring((indx != -1) ? ++indx : 0); - - // get list of attached dbs and for each db, get its size and pagesize - ArrayList<Pair<String, String>> attachedDbs = db.getAttachedDbs(); - if (attachedDbs == null) { - continue; - } - for (int i = 0; i < attachedDbs.size(); i++) { - Pair<String, String> p = attachedDbs.get(i); - long pageCount = DatabaseUtils.longForQuery(db, "PRAGMA " + p.first - + ".page_count;", null); - - // first entry in the attached db list is always the main database - // don't worry about prefixing the dbname with "main" - String dbName; - if (i == 0) { - dbName = lastnode; - } else { - // lookaside is only relevant for the main db - lookasideUsed = 0; - dbName = " (attached) " + p.first; - // if the attached db has a path, attach the lastnode from the path to above - if (p.second.trim().length() > 0) { - int idx = p.second.lastIndexOf("/"); - dbName += " : " + p.second.substring((idx != -1) ? ++idx : 0); - } - } - if (pageCount > 0) { - dbStatsList.add(new DbStats(dbName, pageCount, db.getPageSize(), - lookasideUsed, db.getCacheHitNum(), db.getCacheMissNum(), - db.getCachesize())); - } - } - // if there are pooled connections, return the cache stats for them also. - if (db.mConnectionPool != null) { - for (SQLiteDatabase pDb : db.mConnectionPool.getConnectionList()) { - dbStatsList.add(new DbStats("(pooled # " + pDb.mConnectionNum + ") " - + lastnode, 0, 0, 0, pDb.getCacheHitNum(), - pDb.getCacheMissNum(), pDb.getCachesize())); - } - } - } catch (SQLiteException e) { - // ignore. we don't care about exceptions when we are taking adb - // bugreport! - } - } - } +// // make a local copy of mActiveDatabases - so that this method is not competing +// // for synchronization lock on mActiveDatabases +// ArrayList<WeakReference<SQLiteDatabase>> tempList; +// synchronized(mActiveDatabases) { +// tempList = (ArrayList<WeakReference<SQLiteDatabase>>)mActiveDatabases.clone(); +// } +// for (WeakReference<SQLiteDatabase> w : tempList) { +// SQLiteDatabase db = w.get(); +// if (db == null || !db.isOpen()) { +// continue; +// } +// +// synchronized (db) { +// try { +// // get SQLITE_DBSTATUS_LOOKASIDE_USED for the db +// int lookasideUsed = db.native_getDbLookaside(); +// +// // get the lastnode of the dbname +// String path = db.getPath(); +// int indx = path.lastIndexOf("/"); +// String lastnode = path.substring((indx != -1) ? ++indx : 0); +// +// // get list of attached dbs and for each db, get its size and pagesize +// ArrayList<Pair<String, String>> attachedDbs = db.getAttachedDbs(); +// if (attachedDbs == null) { +// continue; +// } +// for (int i = 0; i < attachedDbs.size(); i++) { +// Pair<String, String> p = attachedDbs.get(i); +// long pageCount = DatabaseUtils.longForQuery(db, "PRAGMA " + p.first +// + ".page_count;", null); +// +// // first entry in the attached db list is always the main database +// // don't worry about prefixing the dbname with "main" +// String dbName; +// if (i == 0) { +// dbName = lastnode; +// } else { +// // lookaside is only relevant for the main db +// lookasideUsed = 0; +// dbName = " (attached) " + p.first; +// // if the attached db has a path, attach the lastnode from the path to above +// if (p.second.trim().length() > 0) { +// int idx = p.second.lastIndexOf("/"); +// dbName += " : " + p.second.substring((idx != -1) ? ++idx : 0); +// } +// } +// if (pageCount > 0) { +// dbStatsList.add(new DbStats(dbName, pageCount, db.getPageSize(), +// lookasideUsed, db.getCacheHitNum(), db.getCacheMissNum(), +// db.getCachesize())); +// } +// } +// // if there are pooled connections, return the cache stats for them also. +// if (db.mConnectionPool != null) { +// for (SQLiteDatabase pDb : db.mConnectionPool.getConnectionList()) { +// dbStatsList.add(new DbStats("(pooled # " + pDb.mConnectionNum + ") " +// + lastnode, 0, 0, 0, pDb.getCacheHitNum(), +// pDb.getCacheMissNum(), pDb.getCachesize())); +// } +// } +// } catch (SQLiteException e) { +// // ignore. we don't care about exceptions when we are taking adb +// // bugreport! +// } +// } +// } return dbStatsList; } diff --git a/core/java/android/net/DownloadManager.java b/core/java/android/net/DownloadManager.java index 12c05cc..3279e8f 100644 --- a/core/java/android/net/DownloadManager.java +++ b/core/java/android/net/DownloadManager.java @@ -214,6 +214,11 @@ public class DownloadManager { "android.intent.action.DOWNLOAD_NOTIFICATION_CLICKED"; /** + * Intent action to launch an activity to display all downloads. + */ + public final static String ACTION_VIEW_DOWNLOADS = "android.intent.action.VIEW_DOWNLOADS"; + + /** * Intent extra included with {@link #ACTION_DOWNLOAD_COMPLETE} intents, indicating the ID (as a * long) of the download that just completed. */ diff --git a/core/java/android/net/NetworkStateTracker.java b/core/java/android/net/NetworkStateTracker.java index 97c31fa..8afdcee 100644 --- a/core/java/android/net/NetworkStateTracker.java +++ b/core/java/android/net/NetworkStateTracker.java @@ -41,6 +41,12 @@ public interface NetworkStateTracker { * ------------------------------------------------------------- */ + // Share the event space with ConnectivityService (which we can't see, but + // must send events to). If you change these, change ConnectivityService + // too. + static final int MIN_NETWORK_STATE_TRACKER_EVENT = 1; + static final int MAX_NETWORK_STATE_TRACKER_EVENT = 100; + /** * The network state has changed and the NetworkInfo object * contains the new state. @@ -63,26 +69,6 @@ public interface NetworkStateTracker { public static final int EVENT_RESTORE_DEFAULT_NETWORK = 6; /** - * USED by ConnectivityService only - * - * msg.what = EVENT_CLEAR_NET_TRANSITION_WAKELOCK - * msg.arg1 = mNetTransitionWakeLockSerialNumber - */ - public static final int EVENT_CLEAR_NET_TRANSITION_WAKELOCK = 7; - - /** - * msg.arg1 = network type - * msg.arg2 = condition (0 bad, 100 good) - */ - public static final int EVENT_INET_CONDITION_CHANGE = 8; - - /** - * msg.arg1 = network type - * msg.arg2 = default connection sequence number - */ - public static final int EVENT_INET_CONDITION_HOLD_END = 9; - - /** * ------------------------------------------------------------- * Control Interface * ------------------------------------------------------------- diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java index ba8014f..d49c8be 100644 --- a/core/java/android/os/BatteryStats.java +++ b/core/java/android/os/BatteryStats.java @@ -301,7 +301,11 @@ public abstract class BatteryStats implements Parcelable { */ public static abstract class Proc { - public static class ExcessiveWake { + public static class ExcessivePower { + public static final int TYPE_WAKE = 1; + public static final int TYPE_CPU = 2; + + public int type; public long overTime; public long usedTime; } @@ -343,9 +347,9 @@ public abstract class BatteryStats implements Parcelable { */ public abstract long getTimeAtCpuSpeedStep(int speedStep, int which); - public abstract int countExcessiveWakes(); + public abstract int countExcessivePowers(); - public abstract ExcessiveWake getExcessiveWake(int i); + public abstract ExcessivePower getExcessivePower(int i); } /** @@ -1593,7 +1597,7 @@ public abstract class BatteryStats implements Parcelable { systemTime = ps.getSystemTime(which); starts = ps.getStarts(which); numExcessive = which == STATS_SINCE_CHARGED - ? ps.countExcessiveWakes() : 0; + ? ps.countExcessivePowers() : 0; if (userTime != 0 || systemTime != 0 || starts != 0 || numExcessive != 0) { @@ -1609,9 +1613,17 @@ public abstract class BatteryStats implements Parcelable { } pw.println(sb.toString()); for (int e=0; e<numExcessive; e++) { - Uid.Proc.ExcessiveWake ew = ps.getExcessiveWake(e); + Uid.Proc.ExcessivePower ew = ps.getExcessivePower(e); if (ew != null) { - pw.print(prefix); pw.print(" * Killed for wake lock use: "); + pw.print(prefix); pw.print(" * Killed for "); + if (ew.type == Uid.Proc.ExcessivePower.TYPE_WAKE) { + pw.print("wake lock"); + } else if (ew.type == Uid.Proc.ExcessivePower.TYPE_CPU) { + pw.print("cpu"); + } else { + pw.print("unknown"); + } + pw.print(" use: "); TimeUtils.formatDuration(ew.usedTime, pw); pw.print(" over "); TimeUtils.formatDuration(ew.overTime, pw); diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java index f695dbb..1235b4d 100644 --- a/core/java/android/os/Process.java +++ b/core/java/android/os/Process.java @@ -86,6 +86,12 @@ public class Process { public static final int WIFI_UID = 1010; /** + * Defines the GID for the group that allows write access to the SD card. + * @hide + */ + public static final int SDCARD_RW_GID = 1015; + + /** * Defines the start of a range of UIDs (and GIDs), going from this * number to {@link #LAST_APPLICATION_UID} that are reserved for assigning * to applications. diff --git a/core/java/android/preference/PreferenceFragment.java b/core/java/android/preference/PreferenceFragment.java index 479497a..7629c31 100644 --- a/core/java/android/preference/PreferenceFragment.java +++ b/core/java/android/preference/PreferenceFragment.java @@ -144,6 +144,7 @@ public abstract class PreferenceFragment extends Fragment implements public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mPreferenceManager = new PreferenceManager(getActivity(), FIRST_REQUEST_CODE); + mPreferenceManager.setFragment(this); mPreferenceManager.setOnPreferenceTreeClickListener(this); } diff --git a/core/java/android/preference/PreferenceManager.java b/core/java/android/preference/PreferenceManager.java index fa83897..42150ed 100644 --- a/core/java/android/preference/PreferenceManager.java +++ b/core/java/android/preference/PreferenceManager.java @@ -16,12 +16,7 @@ package android.preference; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; - import android.app.Activity; -import android.app.Dialog; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; @@ -34,6 +29,10 @@ import android.content.res.XmlResourceParser; import android.os.Bundle; import android.util.Log; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; + /** * Used to help create {@link Preference} hierarchies * from activities or XML. @@ -61,6 +60,11 @@ public class PreferenceManager { private Activity mActivity; /** + * Fragment that owns this instance. + */ + private PreferenceFragment mFragment; + + /** * The context to use. This should always be set. * * @see #mActivity @@ -158,7 +162,21 @@ public class PreferenceManager { setSharedPreferencesName(getDefaultSharedPreferencesName(context)); } - + + /** + * Sets the owning preference fragment + */ + void setFragment(PreferenceFragment fragment) { + mFragment = fragment; + } + + /** + * Returns the owning preference fragment, if any. + */ + PreferenceFragment getFragment() { + return mFragment; + } + /** * Returns a list of {@link Activity} (indirectly) that match a given * {@link Intent}. diff --git a/core/java/android/preference/RingtonePreference.java b/core/java/android/preference/RingtonePreference.java index b46f180..cf14097 100644 --- a/core/java/android/preference/RingtonePreference.java +++ b/core/java/android/preference/RingtonePreference.java @@ -24,7 +24,6 @@ import android.net.Uri; import android.provider.Settings.System; import android.text.TextUtils; import android.util.AttributeSet; -import android.util.Log; /** * A {@link Preference} that allows the user to choose a ringtone from those on the device. @@ -137,7 +136,12 @@ public class RingtonePreference extends Preference implements // Launch the ringtone picker Intent intent = new Intent(RingtoneManager.ACTION_RINGTONE_PICKER); onPrepareRingtonePickerIntent(intent); - getPreferenceManager().getActivity().startActivityForResult(intent, mRequestCode); + PreferenceFragment owningFragment = getPreferenceManager().getFragment(); + if (owningFragment != null) { + owningFragment.startActivityForResult(intent, mRequestCode); + } else { + getPreferenceManager().getActivity().startActivityForResult(intent, mRequestCode); + } } /** diff --git a/core/java/android/provider/AlarmClock.java b/core/java/android/provider/AlarmClock.java new file mode 100644 index 0000000..b93dfd8 --- /dev/null +++ b/core/java/android/provider/AlarmClock.java @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.provider; + +import android.annotation.SdkConstant; +import android.annotation.SdkConstant.SdkConstantType; + +/** + * The AlarmClock provider contains an Intent action and extras that can be used + * to start an Activity to set a new alarm in an alarm clock application. + * + * Applications that wish to receive the ACTION_SET_ALARM Intent should create + * an activity to handle the Intent that requires the permission + * com.android.alarm.permission.SET_ALARM. Applications that wish to create a + * new alarm should use + * {@link android.content.Context#startActivity Context.startActivity()} so that + * the user has the option of choosing which alarm clock application to use. + */ +public final class AlarmClock { + /** + * Activity Action: Set an alarm. + * <p> + * Input: Nothing. + * <p> + * Output: Nothing. + */ + @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) + public static final String ACTION_SET_ALARM = "android.intent.action.SET_ALARM"; + + /** + * Activity Extra: Provide a custom message for the alarm. + * <p> + * This can be passed as an extra field in the Intent created with + * ACTION_SET_ALARM. + */ + public static final String EXTRA_MESSAGE = "android.intent.extra.alarm.MESSAGE"; + + /** + * Activity Extra: The hour of the alarm being set. + * <p> + * This value can be passed as an extra field to the Intent created with + * ACTION_SET_ALARM. If it is not provided, the behavior is undefined and + * is up to the application. The value is an integer and ranges from 0 to + * 23. + */ + public static final String EXTRA_HOUR = "android.intent.extra.alarm.HOUR"; + + /** + * Activity Extra: The minutes of the alarm being set. + * <p> + * This value can be passed as an extra field to the Intent created with + * ACTION_SET_ALARM. If it is not provided, the behavior is undefined and + * is up to the application. The value is an integer and ranges from 0 to + * 59. + */ + public static final String EXTRA_MINUTES = "android.intent.extra.alarm.MINUTES"; +} diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java index 3796994..fbb13af 100644 --- a/core/java/android/view/HardwareRenderer.java +++ b/core/java/android/view/HardwareRenderer.java @@ -19,6 +19,7 @@ package android.view; import android.graphics.Canvas; import android.os.SystemClock; +import android.util.EventLog; import android.util.Log; import javax.microedition.khronos.egl.EGL10; @@ -406,6 +407,11 @@ public abstract class HardwareRenderer { attachInfo.mDrawingTime = SystemClock.uptimeMillis(); attachInfo.mIgnoreDirtyState = true; view.mPrivateFlags |= View.DRAWN; + + long startTime; + if (ViewDebug.DEBUG_PROFILE_DRAWING) { + startTime = SystemClock.elapsedRealtime(); + } checkCurrent(); @@ -423,6 +429,10 @@ public abstract class HardwareRenderer { onPostDraw(); + if (ViewDebug.DEBUG_PROFILE_DRAWING) { + EventLog.writeEvent(60000, SystemClock.elapsedRealtime() - startTime); + } + attachInfo.mIgnoreDirtyState = false; sEgl.eglSwapBuffers(sEglDisplay, mEglSurface); diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index 2b92e87..12c49c4 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -1973,8 +1973,12 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager if (hasNoCache) { final int multipliedAlpha = (int) (255 * alpha); if (!child.onSetAlpha(multipliedAlpha)) { + int layerFlags = Canvas.HAS_ALPHA_LAYER_SAVE_FLAG; + if ((flags & FLAG_CLIP_CHILDREN) == FLAG_CLIP_CHILDREN) { + layerFlags |= Canvas.CLIP_TO_LAYER_SAVE_FLAG; + } canvas.saveLayerAlpha(sx, sy, sx + cr - cl, sy + cb - ct, multipliedAlpha, - Canvas.HAS_ALPHA_LAYER_SAVE_FLAG | Canvas.CLIP_TO_LAYER_SAVE_FLAG); + layerFlags); } else { child.mPrivateFlags |= ALPHA_SET; } diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/ViewRoot.java index 0321be0..f6a06ce 100644 --- a/core/java/android/view/ViewRoot.java +++ b/core/java/android/view/ViewRoot.java @@ -1711,6 +1711,9 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn deliverPointerEvent(event); } finally { event.recycle(); + if (msg.arg1 != 0) { + finishInputEvent(); + } if (LOCAL_LOGV || WATCH_POINTER) Log.i(TAG, "Done dispatching!"); } } break; @@ -1720,6 +1723,9 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn deliverTrackballEvent(event); } finally { event.recycle(); + if (msg.arg1 != 0) { + finishInputEvent(); + } } } break; case DISPATCH_APP_VISIBILITY: @@ -1843,15 +1849,24 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn } } - private void finishKeyEvent(KeyEvent event) { - if (LOCAL_LOGV) Log.v(TAG, "Telling window manager key is finished"); + private void startInputEvent(Runnable finishedCallback) { + if (mFinishedCallback != null) { + Slog.w(TAG, "Received a new input event from the input queue but there is " + + "already an unfinished input event in progress."); + } + + mFinishedCallback = finishedCallback; + } + + private void finishInputEvent() { + if (LOCAL_LOGV) Log.v(TAG, "Telling window manager input event is finished"); if (mFinishedCallback != null) { mFinishedCallback.run(); mFinishedCallback = null; } else { - Slog.w(TAG, "Attempted to tell the input queue that the current key event " - + "is finished but there is no key event actually in progress."); + Slog.w(TAG, "Attempted to tell the input queue that the current input event " + + "is finished but there is no input event actually in progress."); } } @@ -2310,7 +2325,7 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn boolean handled = mView == null || mView.dispatchKeyEventPreIme(event); if (handled) { if (sendDone) { - finishKeyEvent(event); + finishInputEvent(); } return; } @@ -2340,7 +2355,7 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn if (!handled) { deliverKeyEventToViewHierarchy(event, sendDone); } else if (sendDone) { - finishKeyEvent(event); + finishInputEvent(); } else { Log.w(TAG, "handleFinishedEvent(seq=" + seq + " handled=" + handled + " ev=" + event @@ -2413,7 +2428,7 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn } finally { if (sendDone) { - finishKeyEvent(event); + finishInputEvent(); } // Let the exception fall through -- the looper will catch // it and take care of the bad app for us. @@ -2444,6 +2459,7 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn if (DBG) Log.d(TAG, "WindowLayout in layoutWindow:" + params); } mPendingConfiguration.seq = 0; + //Log.d(TAG, ">>>>>> CALLING relayout"); int relayoutResult = sWindowSession.relayout( mWindow, params, (int) (mView.mMeasuredWidth * appScale + 0.5f), @@ -2451,6 +2467,7 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn viewVisibility, insetsPending, mWinFrame, mPendingContentInsets, mPendingVisibleInsets, mPendingConfiguration, mSurface); + //Log.d(TAG, "<<<<<< BACK FROM relayout"); if (restore) { params.restore(); } @@ -2606,20 +2623,13 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn private final InputHandler mInputHandler = new InputHandler() { public void handleKey(KeyEvent event, Runnable finishedCallback) { - if (mFinishedCallback != null) { - Slog.w(TAG, "Received a new key event from the input queue but there is " - + "already an unfinished key event in progress."); - } - - mFinishedCallback = finishedCallback; - + startInputEvent(finishedCallback); dispatchKey(event, true); } public void handleMotion(MotionEvent event, Runnable finishedCallback) { - finishedCallback.run(); - - dispatchMotion(event); + startInputEvent(finishedCallback); + dispatchMotion(event, true); } }; @@ -2651,26 +2661,43 @@ public final class ViewRoot extends Handler implements ViewParent, View.AttachIn } public void dispatchMotion(MotionEvent event) { + dispatchMotion(event, false); + } + + private void dispatchMotion(MotionEvent event, boolean sendDone) { int source = event.getSource(); if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) { - dispatchPointer(event); + dispatchPointer(event, sendDone); } else if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) { - dispatchTrackball(event); + dispatchTrackball(event, sendDone); } else { // TODO Log.v(TAG, "Dropping unsupported motion event (unimplemented): " + event); + if (sendDone) { + finishInputEvent(); + } } } public void dispatchPointer(MotionEvent event) { + dispatchPointer(event, false); + } + + private void dispatchPointer(MotionEvent event, boolean sendDone) { Message msg = obtainMessage(DISPATCH_POINTER); msg.obj = event; + msg.arg1 = sendDone ? 1 : 0; sendMessageAtTime(msg, event.getEventTime()); } public void dispatchTrackball(MotionEvent event) { + dispatchTrackball(event, false); + } + + private void dispatchTrackball(MotionEvent event, boolean sendDone) { Message msg = obtainMessage(DISPATCH_TRACKBALL); msg.obj = event; + msg.arg1 = sendDone ? 1 : 0; sendMessageAtTime(msg, event.getEventTime()); } diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java index bb13f1d..c694ff1 100644 --- a/core/java/android/widget/AbsListView.java +++ b/core/java/android/widget/AbsListView.java @@ -4367,9 +4367,13 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te public void setRemoteViewsAdapter(Intent intent) { // Ensure that we don't already have a RemoteViewsAdapter that is bound to an existing // service handling the specified intent. - Intent.FilterComparison fc = new Intent.FilterComparison(intent); - if (mRemoteAdapter != null && fc.equals(mRemoteAdapter.getRemoteViewsServiceIntent())) { - return; + if (mRemoteAdapter != null) { + Intent.FilterComparison fcNew = new Intent.FilterComparison(intent); + Intent.FilterComparison fcOld = new Intent.FilterComparison( + mRemoteAdapter.getRemoteViewsServiceIntent()); + if (fcNew.equals(fcOld)) { + return; + } } // Otherwise, create a new RemoteViewsAdapter for binding diff --git a/core/java/android/widget/AdapterViewAnimator.java b/core/java/android/widget/AdapterViewAnimator.java index c08adb2..f245933 100644 --- a/core/java/android/widget/AdapterViewAnimator.java +++ b/core/java/android/widget/AdapterViewAnimator.java @@ -389,7 +389,7 @@ public abstract class AdapterViewAnimator extends AdapterView<Adapter> int newWindowStartUnbounded = childIndex - mActiveOffset; int newWindowEndUnbounded = newWindowStartUnbounded + mNumActiveViews - 1; int newWindowStart = Math.max(0, newWindowStartUnbounded); - int newWindowEnd = Math.min(mAdapter.getCount(), newWindowEndUnbounded); + int newWindowEnd = Math.min(mAdapter.getCount() - 1, newWindowEndUnbounded); // This section clears out any items that are in our mActiveViews list // but are outside the effective bounds of our window (this is becomes an issue @@ -592,18 +592,18 @@ public abstract class AdapterViewAnimator extends AdapterView<Adapter> */ private SavedState(Parcel in) { super(in); - whichChild = in.readInt(); + this.whichChild = in.readInt(); } @Override public void writeToParcel(Parcel out, int flags) { super.writeToParcel(out, flags); - out.writeInt(whichChild); + out.writeInt(this.whichChild); } @Override public String toString() { - return "AdapterViewAnimator.SavedState{ whichChild = " + whichChild + " }"; + return "AdapterViewAnimator.SavedState{ whichChild = " + this.whichChild + " }"; } public static final Parcelable.Creator<SavedState> CREATOR @@ -781,10 +781,13 @@ public abstract class AdapterViewAnimator extends AdapterView<Adapter> public void setRemoteViewsAdapter(Intent intent) { // Ensure that we don't already have a RemoteViewsAdapter that is bound to an existing // service handling the specified intent. - Intent.FilterComparison fc = new Intent.FilterComparison(intent); - if (mRemoteViewsAdapter != null && - fc.equals(mRemoteViewsAdapter.getRemoteViewsServiceIntent())) { - return; + if (mRemoteViewsAdapter != null) { + Intent.FilterComparison fcNew = new Intent.FilterComparison(intent); + Intent.FilterComparison fcOld = new Intent.FilterComparison( + mRemoteViewsAdapter.getRemoteViewsServiceIntent()); + if (fcNew.equals(fcOld)) { + return; + } } // Otherwise, create a new RemoteViewsAdapter for binding diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java index 945ffeb..9d214fc 100644 --- a/core/java/android/widget/RemoteViews.java +++ b/core/java/android/widget/RemoteViews.java @@ -74,6 +74,11 @@ public class RemoteViews implements Parcelable, Filter { */ private ArrayList<Action> mActions; + /** + * A class to keep track of memory usage by this RemoteViews + */ + private MemoryUsageCounter mMemoryUsageCounter; + /** * This flag indicates whether this RemoteViews object is being created from a @@ -118,6 +123,15 @@ public class RemoteViews implements Parcelable, Filter { public int describeContents() { return 0; } + + /** + * Overridden by each class to report on it's own memory usage + */ + public void updateMemoryUsageEstimate(MemoryUsageCounter counter) { + // We currently only calculate Bitmap memory usage, so by default, don't do anything + // here + return; + } } private class SetEmptyView extends Action { @@ -525,7 +539,7 @@ public class RemoteViews implements Parcelable, Filter { } } } - + int viewId; boolean targetBackground; int alpha; @@ -817,6 +831,35 @@ public class RemoteViews implements Parcelable, Filter { throw new ActionException(ex); } } + + @Override + public void updateMemoryUsageEstimate(MemoryUsageCounter counter) { + // We currently only calculate Bitmap memory usage + switch (this.type) { + case BITMAP: + if (this.value != null) { + final Bitmap b = (Bitmap) this.value; + final Bitmap.Config c = b.getConfig(); + int bpp = 4; + switch (c) { + case ALPHA_8: + bpp = 1; + break; + case RGB_565: + case ARGB_4444: + bpp = 2; + break; + case ARGB_8888: + bpp = 4; + break; + } + counter.bitmapIncrement(b.getWidth() * b.getHeight() * bpp); + } + break; + default: + break; + } + } } /** @@ -854,6 +897,13 @@ public class RemoteViews implements Parcelable, Filter { } } + @Override + public void updateMemoryUsageEstimate(MemoryUsageCounter counter) { + if (nestedViews != null) { + counter.bitmapIncrement(nestedViews.estimateBitmapMemoryUsage()); + } + } + int viewId; RemoteViews nestedViews; @@ -861,6 +911,26 @@ public class RemoteViews implements Parcelable, Filter { } /** + * Simple class used to keep track of memory usage in a RemoteViews. + * + */ + private class MemoryUsageCounter { + public void clear() { + mBitmapHeapMemoryUsage = 0; + } + + public void bitmapIncrement(int numBytes) { + mBitmapHeapMemoryUsage += numBytes; + } + + public int getBitmapHeapMemoryUsage() { + return mBitmapHeapMemoryUsage; + } + + int mBitmapHeapMemoryUsage; + } + + /** * Create a new RemoteViews object that will display the views contained * in the specified layout file. * @@ -870,6 +940,10 @@ public class RemoteViews implements Parcelable, Filter { public RemoteViews(String packageName, int layoutId) { mPackage = packageName; mLayoutId = layoutId; + + // setup the memory usage statistics + mMemoryUsageCounter = new MemoryUsageCounter(); + recalculateMemoryUsage(); } /** @@ -920,6 +994,10 @@ public class RemoteViews implements Parcelable, Filter { } } } + + // setup the memory usage statistics + mMemoryUsageCounter = new MemoryUsageCounter(); + recalculateMemoryUsage(); } @Override @@ -928,6 +1006,9 @@ public class RemoteViews implements Parcelable, Filter { if (mActions != null) { that.mActions = (ArrayList<Action>)mActions.clone(); } + + // update the memory usage stats of the cloned RemoteViews + that.recalculateMemoryUsage(); return that; } @@ -939,7 +1020,7 @@ public class RemoteViews implements Parcelable, Filter { return mLayoutId; } - /** + /* * This flag indicates whether this RemoteViews object is being created from a * RemoteViewsService for use as a child of a widget collection. This flag is used * to determine whether or not certain features are available, in particular, @@ -951,6 +1032,28 @@ public class RemoteViews implements Parcelable, Filter { } /** + * Updates the memory usage statistics. + */ + private void recalculateMemoryUsage() { + mMemoryUsageCounter.clear(); + + // Accumulate the memory usage for each action + if (mActions != null) { + final int count = mActions.size(); + for (int i= 0; i < count; ++i) { + mActions.get(i).updateMemoryUsageEstimate(mMemoryUsageCounter); + } + } + } + + /** + * Returns an estimate of the bitmap heap memory usage for this RemoteViews. + */ + int estimateBitmapMemoryUsage() { + return mMemoryUsageCounter.getBitmapHeapMemoryUsage(); + } + + /** * Add an action to be executed on the remote side when apply is called. * * @param a The action to add @@ -960,6 +1063,9 @@ public class RemoteViews implements Parcelable, Filter { mActions = new ArrayList<Action>(); } mActions.add(a); + + // update the memory usage stats + a.updateMemoryUsageEstimate(mMemoryUsageCounter); } /** diff --git a/core/java/android/widget/RemoteViewsAdapter.java b/core/java/android/widget/RemoteViewsAdapter.java index 91b4afa..afb56fc 100644 --- a/core/java/android/widget/RemoteViewsAdapter.java +++ b/core/java/android/widget/RemoteViewsAdapter.java @@ -16,7 +16,9 @@ package android.widget; +import java.lang.ref.WeakReference; import java.util.HashMap; +import java.util.HashSet; import java.util.LinkedList; import java.util.Map; @@ -32,8 +34,8 @@ import android.os.Looper; import android.util.Log; import android.view.Gravity; import android.view.View; -import android.view.ViewGroup; import android.view.View.MeasureSpec; +import android.view.ViewGroup; import com.android.internal.widget.IRemoteViewsFactory; @@ -48,15 +50,17 @@ public class RemoteViewsAdapter extends BaseAdapter { private Context mContext; private Intent mIntent; private RemoteViewsAdapterServiceConnection mServiceConnection; - private RemoteViewsCache mViewCache; + private WeakReference<RemoteAdapterConnectionCallback> mCallback; + private FixedSizeRemoteViewsCache mCache; + + // The set of requested views that are to be notified when the associated RemoteViews are + // loaded. + private RemoteViewsFrameLayoutRefSet mRequestedViews; private HandlerThread mWorkerThread; // items may be interrupted within the normally processed queues private Handler mWorkerQueue; private Handler mMainQueue; - // items are never dequeued from the priority queue and must run - private Handler mWorkerPriorityQueue; - private Handler mMainPriorityQueue; /** * An interface for the RemoteAdapter to notify other classes when adapters @@ -70,60 +74,89 @@ public class RemoteViewsAdapter extends BaseAdapter { /** * The service connection that gets populated when the RemoteViewsService is - * bound. + * bound. This must be a static inner class to ensure that no references to the outer + * RemoteViewsAdapter instance is retained (this would prevent the RemoteViewsAdapter from being + * garbage collected, and would cause us to leak activities due to the caching mechanism for + * FrameLayouts in the adapter). */ - private class RemoteViewsAdapterServiceConnection implements ServiceConnection { + private static class RemoteViewsAdapterServiceConnection implements ServiceConnection { private boolean mConnected; + private WeakReference<RemoteViewsAdapter> mAdapter; private IRemoteViewsFactory mRemoteViewsFactory; - private RemoteAdapterConnectionCallback mCallback; - public RemoteViewsAdapterServiceConnection(RemoteAdapterConnectionCallback callback) { - mCallback = callback; + public RemoteViewsAdapterServiceConnection(RemoteViewsAdapter adapter) { + mAdapter = new WeakReference<RemoteViewsAdapter>(adapter); } - public void onServiceConnected(ComponentName name, IBinder service) { + public void onServiceConnected(ComponentName name, + IBinder service) { mRemoteViewsFactory = IRemoteViewsFactory.Stub.asInterface(service); mConnected = true; - // notifyDataSetChanged should be called first, to ensure that the - // views are not updated twice - notifyDataSetChanged(); - - // post a new runnable to load the appropriate data, then callback - mWorkerPriorityQueue.post(new Runnable() { + // Queue up work that we need to do for the callback to run + final RemoteViewsAdapter adapter = mAdapter.get(); + if (adapter == null) return; + adapter.mWorkerQueue.post(new Runnable() { @Override public void run() { - // we need to get the viewTypeCount specifically, so just get all the - // metadata - mViewCache.requestMetaData(); - - // post a runnable to call the callback on the main thread - mMainPriorityQueue.post(new Runnable() { - @Override - public void run() { - if (mCallback != null) - mCallback.onRemoteAdapterConnected(); + // Call back to the service to notify that the data set changed + if (adapter.mServiceConnection.isConnected()) { + IRemoteViewsFactory factory = + adapter.mServiceConnection.getRemoteViewsFactory(); + try { + // call back to the factory + factory.onDataSetChanged(); + } catch (Exception e) { + Log.e(TAG, "Error notifying factory of data set changed in " + + "onServiceConnected(): " + e.getMessage()); + e.printStackTrace(); + + // Return early to prevent anything further from being notified + // (effectively nothing has changed) + return; } - }); + + // Request meta data so that we have up to date data when calling back to + // the remote adapter callback + adapter.updateMetaData(); + + // Post a runnable to call back to the view to notify it that we have + // connected + adapter. mMainQueue.post(new Runnable() { + @Override + public void run() { + final RemoteAdapterConnectionCallback callback = + adapter.mCallback.get(); + if (callback != null) { + callback.onRemoteAdapterConnected(); + } + } + }); + } } }); - - // start the background loader - mViewCache.startBackgroundLoader(); } public void onServiceDisconnected(ComponentName name) { - mRemoteViewsFactory = null; mConnected = false; + mRemoteViewsFactory = null; - // clear the main/worker queues - mMainQueue.removeMessages(0); + final RemoteViewsAdapter adapter = mAdapter.get(); + if (adapter == null) return; - // stop the background loader - mViewCache.stopBackgroundLoader(); + // Clear the main/worker queues + adapter.mMainQueue.removeMessages(0); + adapter.mWorkerQueue.removeMessages(0); + + // Clear the cache + synchronized (adapter.mCache) { + adapter.mCache.reset(); + } - if (mCallback != null) - mCallback.onRemoteAdapterDisconnected(); + final RemoteAdapterConnectionCallback callback = adapter.mCallback.get(); + if (callback != null) { + callback.onRemoteAdapterDisconnected(); + } } public IRemoteViewsFactory getRemoteViewsFactory() { @@ -136,532 +169,400 @@ public class RemoteViewsAdapter extends BaseAdapter { } /** - * An internal cache of remote views. + * A FrameLayout which contains a loading view, and manages the re/applying of RemoteViews when + * they are loaded. */ - private class RemoteViewsCache { - private static final String TAG = "RemoteViewsCache"; - - private RemoteViewsInfo mViewCacheInfo; - private RemoteViewsIndexInfo[] mViewCache; - private int[] mTmpViewCacheLoadIndices; - private LinkedList<Integer> mViewCacheLoadIndices; - private boolean mBackgroundLoaderEnabled; - - // if a user loading view is not provided, then we create a temporary one - // for the user using the height of the first view - private RemoteViews mUserLoadingView; - private RemoteViews mFirstView; - private int mFirstViewHeight; - - // determines when the current cache window needs to be updated with new - // items (ie. when there is not enough slack) - private int mViewCacheStartPosition; - private int mViewCacheEndPosition; - private int mHalfCacheSize; - private int mCacheSlack; - private final float mCacheSlackPercentage = 0.75f; + private class RemoteViewsFrameLayout extends FrameLayout { + public RemoteViewsFrameLayout(Context context) { + super(context); + } /** - * The data structure stored at each index of the cache. Any member - * that is not invalidated persists throughout the lifetime of the cache. + * Updates this RemoteViewsFrameLayout depending on the view that was loaded. + * @param view the RemoteViews that was loaded. If null, the RemoteViews was not loaded + * successfully. */ - private class RemoteViewsIndexInfo { - FrameLayout flipper; - RemoteViews view; - long itemId; - int typeId; - - RemoteViewsIndexInfo() { - invalidate(); - } - - void set(RemoteViews v, long id) { - view = v; - itemId = id; - if (v != null) - typeId = v.getLayoutId(); - else - typeId = 0; - } + public void onRemoteViewsLoaded(RemoteViews view) { + // Remove all the children of this layout first + removeAllViews(); + addView(view.apply(getContext(), this)); + } + } - void invalidate() { - view = null; - itemId = 0; - typeId = 0; - } + /** + * Stores the references of all the RemoteViewsFrameLayouts that have been returned by the + * adapter that have not yet had their RemoteViews loaded. + */ + private class RemoteViewsFrameLayoutRefSet { + private HashMap<Integer, LinkedList<RemoteViewsFrameLayout>> mReferences; - final boolean isValid() { - return (view != null); - } + public RemoteViewsFrameLayoutRefSet() { + mReferences = new HashMap<Integer, LinkedList<RemoteViewsFrameLayout>>(); } /** - * Remote adapter metadata. Useful for when we have to lock on something - * before updating the metadata. + * Adds a new reference to a RemoteViewsFrameLayout returned by the adapter. */ - private class RemoteViewsInfo { - int count; - int viewTypeCount; - boolean hasStableIds; - boolean isDataDirty; - Map<Integer, Integer> mTypeIdIndexMap; - - RemoteViewsInfo() { - count = 0; - // by default there is at least one dummy view type - viewTypeCount = 1; - hasStableIds = true; - isDataDirty = false; - mTypeIdIndexMap = new HashMap<Integer, Integer>(); + public void add(int position, RemoteViewsFrameLayout layout) { + final Integer pos = position; + LinkedList<RemoteViewsFrameLayout> refs; + + // Create the list if necessary + if (mReferences.containsKey(pos)) { + refs = mReferences.get(pos); + } else { + refs = new LinkedList<RemoteViewsFrameLayout>(); + mReferences.put(pos, refs); } + + // Add the references to the list + refs.add(layout); } - public RemoteViewsCache(int halfCacheSize) { - mHalfCacheSize = halfCacheSize; - mCacheSlack = Math.round(mCacheSlackPercentage * mHalfCacheSize); - mViewCacheStartPosition = 0; - mViewCacheEndPosition = -1; - mBackgroundLoaderEnabled = false; + /** + * Notifies each of the RemoteViewsFrameLayouts associated with a particular position that + * the associated RemoteViews has loaded. + */ + public void notifyOnRemoteViewsLoaded(int position, RemoteViews view, int typeId) { + final Integer pos = position; + if (mReferences.containsKey(pos)) { + // Notify all the references for that position of the newly loaded RemoteViews + final LinkedList<RemoteViewsFrameLayout> refs = mReferences.get(pos); + for (final RemoteViewsFrameLayout ref : refs) { + ref.onRemoteViewsLoaded(view); + } + refs.clear(); - // initialize the cache - int cacheSize = 2 * mHalfCacheSize + 1; - mViewCacheInfo = new RemoteViewsInfo(); - mViewCache = new RemoteViewsIndexInfo[cacheSize]; - for (int i = 0; i < mViewCache.length; ++i) { - mViewCache[i] = new RemoteViewsIndexInfo(); + // Remove this set from the original mapping + mReferences.remove(pos); } - mTmpViewCacheLoadIndices = new int[cacheSize]; - mViewCacheLoadIndices = new LinkedList<Integer>(); } - private final boolean contains(int position) { - return (mViewCacheStartPosition <= position) && (position <= mViewCacheEndPosition); + /** + * Removes all references to all RemoteViewsFrameLayouts returned by the adapter. + */ + public void clear() { + // We currently just clear the references, and leave all the previous layouts returned + // in their default state of the loading view. + mReferences.clear(); } + } - private final boolean containsAndIsValid(int position) { - if (contains(position)) { - RemoteViewsIndexInfo indexInfo = mViewCache[getCacheIndex(position)]; - if (indexInfo.isValid()) { - return true; - } - } - return false; + /** + * The meta-data associated with the cache in it's current state. + */ + private class RemoteViewsMetaData { + int count; + int viewTypeCount; + boolean hasStableIds; + boolean isDataDirty; + + // Used to determine how to construct loading views. If a loading view is not specified + // by the user, then we try and load the first view, and use its height as the height for + // the default loading view. + RemoteViews mUserLoadingView; + RemoteViews mFirstView; + int mFirstViewHeight; + + // A mapping from type id to a set of unique type ids + private Map<Integer, Integer> mTypeIdIndexMap; + + public RemoteViewsMetaData() { + reset(); } - private final int getCacheIndex(int position) { - // take the modulo of the position - final int cacheSize = mViewCache.length; - return (cacheSize + (position % cacheSize)) % cacheSize; + public void reset() { + count = 0; + // by default there is at least one dummy view type + viewTypeCount = 1; + hasStableIds = true; + isDataDirty = false; + mUserLoadingView = null; + mFirstView = null; + mFirstViewHeight = 0; + mTypeIdIndexMap = new HashMap<Integer, Integer>(); } - public void requestMetaData() { - if (mServiceConnection.isConnected()) { - try { - IRemoteViewsFactory factory = mServiceConnection.getRemoteViewsFactory(); - - // get the properties/first view (so that we can use it to - // measure our dummy views) - boolean hasStableIds = factory.hasStableIds(); - int viewTypeCount = factory.getViewTypeCount(); - int count = factory.getCount(); - RemoteViews loadingView = factory.getLoadingView(); - RemoteViews firstView = null; - if ((count > 0) && (loadingView == null)) { - firstView = factory.getViewAt(0); - } - synchronized (mViewCacheInfo) { - RemoteViewsInfo info = mViewCacheInfo; - info.hasStableIds = hasStableIds; - info.viewTypeCount = viewTypeCount + 1; - info.count = count; - mUserLoadingView = loadingView; - if (firstView != null) { - mFirstView = firstView; - mFirstViewHeight = -1; - } - } - } catch (Exception e) { - // print the error - Log.e(TAG, "Error in requestMetaData(): " + e.getMessage()); - - // reset any members after the failed call - synchronized (mViewCacheInfo) { - RemoteViewsInfo info = mViewCacheInfo; - info.hasStableIds = false; - info.viewTypeCount = 1; - info.count = 0; - mUserLoadingView = null; - mFirstView = null; - mFirstViewHeight = -1; - } - } + public void setLoadingViewTemplates(RemoteViews loadingView, RemoteViews firstView) { + mUserLoadingView = loadingView; + if (firstView != null) { + mFirstView = firstView; + mFirstViewHeight = -1; } } - protected void onNotifyDataSetChanged() { - // we mark the data as dirty so that the next call to fetch views will result in - // an onDataSetDirty() call from the adapter - synchronized (mViewCacheInfo) { - mViewCacheInfo.isDataDirty = true; + public int getMappedViewType(int typeId) { + if (mTypeIdIndexMap.containsKey(typeId)) { + return mTypeIdIndexMap.get(typeId); + } else { + // We +1 because the loading view always has view type id of 0 + int incrementalTypeId = mTypeIdIndexMap.size() + 1; + mTypeIdIndexMap.put(typeId, incrementalTypeId); + return incrementalTypeId; } } - private void updateNotifyDataSetChanged() { - // actually calls through to the factory to notify it to update - if (mServiceConnection.isConnected()) { - IRemoteViewsFactory factory = mServiceConnection.getRemoteViewsFactory(); - try { - // call back to the factory - factory.onDataSetChanged(); - } catch (Exception e) { - // print the error - Log.e(TAG, "Error in updateNotifyDataSetChanged(): " + e.getMessage()); - - // return early to prevent container from being notified (nothing has changed) - return; + private RemoteViewsFrameLayout createLoadingView(int position, View convertView, + ViewGroup parent) { + // Create and return a new FrameLayout, and setup the references for this position + final Context context = parent.getContext(); + RemoteViewsFrameLayout layout = new RemoteViewsFrameLayout(context); + + // Create a new loading view + synchronized (mCache) { + if (mUserLoadingView != null) { + // A user-specified loading view + View loadingView = mUserLoadingView.apply(parent.getContext(), parent); + loadingView.setTag(new Integer(0)); + layout.addView(loadingView); + } else { + // A default loading view + // Use the size of the first row as a guide for the size of the loading view + if (mFirstViewHeight < 0) { + View firstView = mFirstView.apply(parent.getContext(), parent); + firstView.measure( + MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), + MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)); + mFirstViewHeight = firstView.getMeasuredHeight(); + mFirstView = null; + } + + // Compose the loading view text + TextView textView = new TextView(parent.getContext()); + textView.setText(com.android.internal.R.string.loading); + textView.setHeight(mFirstViewHeight); + textView.setGravity(Gravity.CENTER_HORIZONTAL | Gravity.CENTER_VERTICAL); + textView.setTextSize(18.0f); + textView.setTextColor(Color.argb(96, 255, 255, 255)); + textView.setShadowLayer(2.0f, 0.0f, 1.0f, Color.BLACK); + textView.setTag(new Integer(0)); + + layout.addView(textView); } } - // re-request the new metadata (only after the notification to the factory) - requestMetaData(); - - // post a new runnable on the main thread to propagate the notification back - // to the base adapter - mMainQueue.post(new Runnable() { - @Override - public void run() { - completeNotifyDataSetChanged(); - } - }); + return layout; } + } - protected void updateRemoteViewsInfo(int position) { - if (mServiceConnection.isConnected()) { - IRemoteViewsFactory factory = mServiceConnection.getRemoteViewsFactory(); - - // load the item information - RemoteViews remoteView = null; - long itemId = 0; - try { - remoteView = factory.getViewAt(position); - itemId = factory.getItemId(position); - } catch (Exception e) { - // print the error - Log.e(TAG, "Error in updateRemoteViewsInfo(" + position + "): " + - e.getMessage()); - e.printStackTrace(); - - // return early to prevent additional work in re-centering the view cache, and - // swapping from the loading view - return; - } + /** + * The meta-data associated with a single item in the cache. + */ + private class RemoteViewsIndexMetaData { + int typeId; + long itemId; - synchronized (mViewCache) { - // skip if the window has moved - if (position < mViewCacheStartPosition || position > mViewCacheEndPosition) - return; - - final int positionIndex = position; - final int cacheIndex = getCacheIndex(position); - mViewCache[cacheIndex].set(remoteView, itemId); - - // notify the main thread when done loading - // flush pending updates - mMainQueue.post(new Runnable() { - @Override - public void run() { - // swap the loader view for this view - synchronized (mViewCache) { - if (containsAndIsValid(positionIndex)) { - RemoteViewsIndexInfo indexInfo = mViewCache[cacheIndex]; - FrameLayout flipper = indexInfo.flipper; - - // update the flipper - flipper.getChildAt(0).setVisibility(View.GONE); - boolean addNewView = true; - if (flipper.getChildCount() > 1) { - View v = flipper.getChildAt(1); - int typeId = ((Integer) v.getTag()).intValue(); - if (typeId == indexInfo.typeId) { - // we can reapply since it is the same type - indexInfo.view.reapply(mContext, v); - v.setVisibility(View.VISIBLE); - if (v.getAnimation() != null) - v.buildDrawingCache(); - addNewView = false; - } else { - flipper.removeViewAt(1); - } - } - if (addNewView) { - View v = indexInfo.view.apply(mContext, flipper); - v.setTag(new Integer(indexInfo.typeId)); - flipper.addView(v); - } - } - } - } - }); - } - } + public RemoteViewsIndexMetaData(RemoteViews v, long itemId) { + set(v, itemId); } - private RemoteViewsIndexInfo requestCachedIndexInfo(final int position) { - int indicesToLoadCount = 0; - - synchronized (mViewCache) { - if (containsAndIsValid(position)) { - // return the info if it exists in the window and is loaded - return mViewCache[getCacheIndex(position)]; - } + public void set(RemoteViews v, long id) { + itemId = id; + if (v != null) + typeId = v.getLayoutId(); + else + typeId = 0; + } + } - // if necessary update the window and load the new information - int centerPosition = (mViewCacheEndPosition + mViewCacheStartPosition) / 2; - if ((mViewCacheEndPosition <= mViewCacheStartPosition) || (Math.abs(position - centerPosition) > mCacheSlack)) { - int newStartPosition = position - mHalfCacheSize; - int newEndPosition = position + mHalfCacheSize; - int frameSize = mHalfCacheSize / 4; - int frameCount = (int) Math.ceil(mViewCache.length / (float) frameSize); - - // prune/add before the current start position - int effectiveStart = Math.max(newStartPosition, 0); - int effectiveEnd = Math.min(newEndPosition, getCount() - 1); - - // invalidate items in the queue - int overlapStart = Math.max(mViewCacheStartPosition, effectiveStart); - int overlapEnd = Math.min(Math.max(mViewCacheStartPosition, mViewCacheEndPosition), effectiveEnd); - for (int i = 0; i < (frameSize * frameCount); ++i) { - int index = newStartPosition + ((i % frameSize) * frameCount + (i / frameSize)); - - if (index <= newEndPosition) { - if ((overlapStart <= index) && (index <= overlapEnd)) { - // load the stuff in the middle that has not already - // been loaded - if (!mViewCache[getCacheIndex(index)].isValid()) { - mTmpViewCacheLoadIndices[indicesToLoadCount++] = index; - } - } else if ((effectiveStart <= index) && (index <= effectiveEnd)) { - // invalidate and load all new effective items - mViewCache[getCacheIndex(index)].invalidate(); - mTmpViewCacheLoadIndices[indicesToLoadCount++] = index; - } else { - // invalidate all other cache indices (outside the effective start/end) - // but don't load - mViewCache[getCacheIndex(index)].invalidate(); - } - } - } + /** + * + */ + private class FixedSizeRemoteViewsCache { + private static final String TAG = "FixedSizeRemoteViewsCache"; + + // The meta data related to all the RemoteViews, ie. count, is stable, etc. + private RemoteViewsMetaData mMetaData; + + // The cache/mapping of position to RemoteViewsMetaData. This set is guaranteed to be + // greater than or equal to the set of RemoteViews. + // Note: The reason that we keep this separate from the RemoteViews cache below is that this + // we still need to be able to access the mapping of position to meta data, without keeping + // the heavy RemoteViews around. The RemoteViews cache is trimmed to fixed constraints wrt. + // memory and size, but this metadata cache will retain information until the data at the + // position is guaranteed as not being necessary any more (usually on notifyDataSetChanged). + private HashMap<Integer, RemoteViewsIndexMetaData> mIndexMetaData; + + // The cache of actual RemoteViews, which may be pruned if the cache gets too large, or uses + // too much memory. + private HashMap<Integer, RemoteViews> mIndexRemoteViews; + + // The set of indices that have been explicitly requested by the collection view + private HashSet<Integer> mRequestedIndices; + + // The set of indices to load, including those explicitly requested, as well as those + // determined by the preloading algorithm to be prefetched + private HashSet<Integer> mLoadIndices; + + // The lower and upper bounds of the preloaded range + private int mPreloadLowerBound; + private int mPreloadUpperBound; + + // The bounds of this fixed cache, we will try and fill as many items into the cache up to + // the maxCount number of items, or the maxSize memory usage. + // The maxCountSlack is used to determine if a new position in the cache to be loaded is + // sufficiently ouside the old set, prompting a shifting of the "window" of items to be + // preloaded. + private int mMaxCount; + private int mMaxCountSlack; + private static final float sMaxCountSlackPercent = 0.75f; + private static final int sMaxMemoryUsage = 1024 * 1024; + + public FixedSizeRemoteViewsCache(int maxCacheSize) { + mMaxCount = maxCacheSize; + mMaxCountSlack = Math.round(sMaxCountSlackPercent * (mMaxCount / 2)); + mPreloadLowerBound = 0; + mPreloadUpperBound = -1; + mMetaData = new RemoteViewsMetaData(); + mIndexMetaData = new HashMap<Integer, RemoteViewsIndexMetaData>(); + mIndexRemoteViews = new HashMap<Integer, RemoteViews>(); + mRequestedIndices = new HashSet<Integer>(); + mLoadIndices = new HashSet<Integer>(); + } - mViewCacheStartPosition = newStartPosition; - mViewCacheEndPosition = newEndPosition; - } + public void insert(int position, RemoteViews v, long itemId) { + // Trim the cache if we go beyond the count + if (mIndexRemoteViews.size() >= mMaxCount) { + mIndexRemoteViews.remove(getFarthestPositionFrom(position)); } - // post items to be loaded - int length = 0; - synchronized (mViewCacheInfo) { - length = mViewCacheInfo.count; + // Trim the cache if we go beyond the available memory size constraints + while (getRemoteViewsBitmapMemoryUsage() >= sMaxMemoryUsage) { + // Note: This is currently the most naive mechanism for deciding what to prune when + // we hit the memory limit. In the future, we may want to calculate which index to + // remove based on both its position as well as it's current memory usage, as well + // as whether it was directly requested vs. whether it was preloaded by our caching + // mechanism. + mIndexRemoteViews.remove(getFarthestPositionFrom(position)); } - if (indicesToLoadCount > 0) { - synchronized (mViewCacheLoadIndices) { - mViewCacheLoadIndices.clear(); - for (int i = 0; i < indicesToLoadCount; ++i) { - final int index = mTmpViewCacheLoadIndices[i]; - if (0 <= index && index < length) { - mViewCacheLoadIndices.addLast(index); - } - } - } + + // Update the metadata cache + if (mIndexMetaData.containsKey(position)) { + final RemoteViewsIndexMetaData metaData = mIndexMetaData.get(position); + metaData.set(v, itemId); + } else { + mIndexMetaData.put(position, new RemoteViewsIndexMetaData(v, itemId)); } + mIndexRemoteViews.put(position, v); + } - // return null so that a dummy view can be retrieved + public RemoteViewsMetaData getMetaData() { + return mMetaData; + } + public RemoteViews getRemoteViewsAt(int position) { + if (mIndexRemoteViews.containsKey(position)) { + return mIndexRemoteViews.get(position); + } return null; } - - public View getView(int position, View convertView, ViewGroup parent) { - if (mServiceConnection.isConnected()) { - // create the flipper views if necessary (we have to do this now - // for all the flippers while we have the reference to the parent) - initializeLoadingViews(parent); - - // request the item from the cache (queueing it to load if not - // in the cache already) - RemoteViewsIndexInfo indexInfo = requestCachedIndexInfo(position); - - // update the flipper appropriately - synchronized (mViewCache) { - int cacheIndex = getCacheIndex(position); - FrameLayout flipper = mViewCache[cacheIndex].flipper; - flipper.setVisibility(View.VISIBLE); - flipper.setAlpha(1.0f); - - if (indexInfo == null) { - // hide the item view and show the loading view - flipper.getChildAt(0).setVisibility(View.VISIBLE); - for (int i = 1; i < flipper.getChildCount(); ++i) { - flipper.getChildAt(i).setVisibility(View.GONE); - } - } else { - // hide the loading view and show the item view - for (int i = 0; i < flipper.getChildCount() - 1; ++i) { - flipper.getChildAt(i).setVisibility(View.GONE); - } - flipper.getChildAt(flipper.getChildCount() - 1).setVisibility(View.VISIBLE); - } - return flipper; - } + public RemoteViewsIndexMetaData getMetaDataAt(int position) { + if (mIndexMetaData.containsKey(position)) { + return mIndexMetaData.get(position); } - return new View(mContext); - } - - private void initializeLoadingViews(ViewGroup parent) { - // ensure that the cache has the appropriate initial flipper - synchronized (mViewCache) { - if (mViewCache[0].flipper == null) { - for (int i = 0; i < mViewCache.length; ++i) { - FrameLayout flipper = new FrameLayout(mContext); - if (mUserLoadingView != null) { - // use the user-specified loading view - flipper.addView(mUserLoadingView.apply(mContext, parent)); - } else { - // calculate the original size of the first row for the loader view - synchronized (mViewCacheInfo) { - if (mFirstViewHeight < 0) { - View firstView = mFirstView.apply(mContext, parent); - firstView.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), - MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)); - mFirstViewHeight = firstView.getMeasuredHeight(); - } - } + return null; + } - // construct a new loader and add it to the flipper as the fallback - // default view - TextView textView = new TextView(mContext); - textView.setText("Loading..."); - textView.setHeight(mFirstViewHeight); - textView.setGravity(Gravity.CENTER_HORIZONTAL | Gravity.CENTER_VERTICAL); - textView.setTextSize(18.0f); - textView.setTextColor(Color.argb(96, 255, 255, 255)); - textView.setShadowLayer(2.0f, 0.0f, 1.0f, Color.BLACK); - - flipper.addView(textView); - } - mViewCache[i].flipper = flipper; - } - } + private int getRemoteViewsBitmapMemoryUsage() { + // Calculate the memory usage of all the RemoteViews bitmaps being cached + int mem = 0; + for (Integer i : mIndexRemoteViews.keySet()) { + final RemoteViews v = mIndexRemoteViews.get(i); + mem += v.estimateBitmapMemoryUsage(); } + return mem; } - - public void startBackgroundLoader() { - // initialize the worker runnable - mBackgroundLoaderEnabled = true; - mWorkerQueue.post(new Runnable() { - @Override - public void run() { - while (mBackgroundLoaderEnabled) { - // notify the RemoteViews factory if necessary - boolean isDataDirty = false; - synchronized (mViewCacheInfo) { - isDataDirty = mViewCacheInfo.isDataDirty; - mViewCacheInfo.isDataDirty = false; - } - if (isDataDirty) { - updateNotifyDataSetChanged(); - } - - int index = -1; - synchronized (mViewCacheLoadIndices) { - if (!mViewCacheLoadIndices.isEmpty()) { - index = mViewCacheLoadIndices.removeFirst(); - } - } - if (index < 0) { - // there were no items to load, so sleep for a bit - try { - Thread.sleep(10); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } else { - // otherwise, try and load the item - updateRemoteViewsInfo(index); - } - } + private int getFarthestPositionFrom(int pos) { + // Find the index farthest away and remove that + int maxDist = 0; + int maxDistIndex = -1; + for (int i : mIndexRemoteViews.keySet()) { + int dist = Math.abs(i-pos); + if (dist > maxDist) { + maxDistIndex = i; + maxDist = dist; } - }); - } - - public void stopBackgroundLoader() { - // clear the items to be loaded - mBackgroundLoaderEnabled = false; - synchronized (mViewCacheLoadIndices) { - mViewCacheLoadIndices.clear(); } + return maxDistIndex; } - public long getItemId(int position) { - synchronized (mViewCache) { - if (containsAndIsValid(position)) { - return mViewCache[getCacheIndex(position)].itemId; - } + public void queueRequestedPositionToLoad(int position) { + synchronized (mLoadIndices) { + mRequestedIndices.add(position); + mLoadIndices.add(position); } - return 0; } - - public int getItemViewType(int position) { - // synchronize to ensure that the type id/index map is updated synchronously - synchronized (mViewCache) { - if (containsAndIsValid(position)) { - int viewId = mViewCache[getCacheIndex(position)].typeId; - Map<Integer, Integer> typeMap = mViewCacheInfo.mTypeIdIndexMap; - // we +1 because the default dummy view get view type 0 - if (typeMap.containsKey(viewId)) { - return typeMap.get(viewId); - } else { - int newIndex = typeMap.size() + 1; - typeMap.put(viewId, newIndex); - return newIndex; - } + public void queuePositionsToBePreloadedFromRequestedPosition(int position) { + // Check if we need to preload any items + if (mPreloadLowerBound <= position && position <= mPreloadUpperBound) { + int center = (mPreloadUpperBound + mPreloadLowerBound) / 2; + if (Math.abs(position - center) < mMaxCountSlack) { + return; } } - // return the type of the default item - return 0; - } - public int getCount() { - synchronized (mViewCacheInfo) { - return mViewCacheInfo.count; + int count = 0; + synchronized (mMetaData) { + count = mMetaData.count; } - } + synchronized (mLoadIndices) { + mLoadIndices.clear(); + + // Add all the requested indices + mLoadIndices.addAll(mRequestedIndices); + + // Add all the preload indices + int halfMaxCount = mMaxCount / 2; + mPreloadLowerBound = position - halfMaxCount; + mPreloadUpperBound = position + halfMaxCount; + int effectiveLowerBound = Math.max(0, mPreloadLowerBound); + int effectiveUpperBound = Math.min(mPreloadUpperBound, count - 1); + for (int i = effectiveLowerBound; i <= effectiveUpperBound; ++i) { + mLoadIndices.add(i); + } - public int getViewTypeCount() { - synchronized (mViewCacheInfo) { - return mViewCacheInfo.viewTypeCount; + // But remove all the indices that have already been loaded and are cached + mLoadIndices.removeAll(mIndexRemoteViews.keySet()); } } + public int getNextIndexToLoad() { + // We try and prioritize items that have been requested directly, instead + // of items that are loaded as a result of the caching mechanism + synchronized (mLoadIndices) { + // Prioritize requested indices to be loaded first + if (!mRequestedIndices.isEmpty()) { + Integer i = mRequestedIndices.iterator().next(); + mRequestedIndices.remove(i); + mLoadIndices.remove(i); + return i.intValue(); + } - public boolean hasStableIds() { - synchronized (mViewCacheInfo) { - return mViewCacheInfo.hasStableIds; - } - } + // Otherwise, preload other indices as necessary + if (!mLoadIndices.isEmpty()) { + Integer i = mLoadIndices.iterator().next(); + mLoadIndices.remove(i); + return i.intValue(); + } - public void flushCache() { - // clear the items to be loaded - synchronized (mViewCacheLoadIndices) { - mViewCacheLoadIndices.clear(); + return -1; } + } - synchronized (mViewCache) { - // flush the internal cache and invalidate the adapter for future loads - mMainQueue.removeMessages(0); - - for (int i = 0; i < mViewCache.length; ++i) { - mViewCache[i].invalidate(); - } + public boolean containsRemoteViewAt(int position) { + return mIndexRemoteViews.containsKey(position); + } + public boolean containsMetaDataAt(int position) { + return mIndexMetaData.containsKey(position); + } - mViewCacheStartPosition = 0; - mViewCacheEndPosition = -1; + public void reset() { + mPreloadLowerBound = 0; + mPreloadUpperBound = -1; + mIndexRemoteViews.clear(); + mIndexMetaData.clear(); + mMetaData.reset(); + synchronized (mLoadIndices) { + mRequestedIndices.clear(); + mLoadIndices.clear(); } } } @@ -672,24 +573,126 @@ public class RemoteViewsAdapter extends BaseAdapter { if (mIntent == null) { throw new IllegalArgumentException("Non-null Intent must be specified."); } + mRequestedViews = new RemoteViewsFrameLayoutRefSet(); // initialize the worker thread mWorkerThread = new HandlerThread("RemoteViewsCache-loader"); mWorkerThread.start(); mWorkerQueue = new Handler(mWorkerThread.getLooper()); - mWorkerPriorityQueue = new Handler(mWorkerThread.getLooper()); mMainQueue = new Handler(Looper.myLooper()); - mMainPriorityQueue = new Handler(Looper.myLooper()); // initialize the cache and the service connection on startup - mViewCache = new RemoteViewsCache(25); - mServiceConnection = new RemoteViewsAdapterServiceConnection(callback); + mCache = new FixedSizeRemoteViewsCache(50); + mCallback = new WeakReference<RemoteAdapterConnectionCallback>(callback); + mServiceConnection = new RemoteViewsAdapterServiceConnection(this); requestBindService(); } - protected void finalize() throws Throwable { - // remember to unbind from the service when finalizing - unbindService(); + private void loadNextIndexInBackground() { + mWorkerQueue.post(new Runnable() { + @Override + public void run() { + boolean isDataDirty = false; + + // If the data set has changed, then notify the remote factory so that it can + // update its internals first. + final RemoteViewsMetaData metaData = mCache.getMetaData(); + synchronized (metaData) { + isDataDirty = metaData.isDataDirty; + metaData.isDataDirty = false; + } + if (isDataDirty) { + completeNotifyDataSetChanged(); + } + + // Get the next index to load + int position = -1; + synchronized (mCache) { + position = mCache.getNextIndexToLoad(); + } + if (position > -1) { + // Load the item, and notify any existing RemoteViewsFrameLayouts + updateRemoteViews(position); + + // Queue up for the next one to load + loadNextIndexInBackground(); + } + } + }); + } + + private void updateMetaData() { + if (mServiceConnection.isConnected()) { + try { + IRemoteViewsFactory factory = mServiceConnection.getRemoteViewsFactory(); + + // get the properties/first view (so that we can use it to + // measure our dummy views) + boolean hasStableIds = factory.hasStableIds(); + int viewTypeCount = factory.getViewTypeCount(); + int count = factory.getCount(); + RemoteViews loadingView = factory.getLoadingView(); + RemoteViews firstView = null; + if ((count > 0) && (loadingView == null)) { + firstView = factory.getViewAt(0); + } + final RemoteViewsMetaData metaData = mCache.getMetaData(); + synchronized (metaData) { + metaData.hasStableIds = hasStableIds; + metaData.viewTypeCount = viewTypeCount + 1; + metaData.count = count; + metaData.setLoadingViewTemplates(loadingView, firstView); + } + } catch (Exception e) { + // print the error + Log.e(TAG, "Error in requestMetaData(): " + e.getMessage()); + + // reset any members after the failed call + final RemoteViewsMetaData metaData = mCache.getMetaData(); + synchronized (metaData) { + metaData.reset(); + } + } + } + } + + private void updateRemoteViews(final int position) { + if (mServiceConnection.isConnected()) { + IRemoteViewsFactory factory = mServiceConnection.getRemoteViewsFactory(); + + // Load the item information from the remote service + RemoteViews remoteViews = null; + long itemId = 0; + try { + remoteViews = factory.getViewAt(position); + itemId = factory.getItemId(position); + } catch (Exception e) { + // Print the error + Log.e(TAG, "Error in updateRemoteViewsInfo(" + position + "): " + + e.getMessage()); + e.printStackTrace(); + + // Return early to prevent additional work in re-centering the view cache, and + // swapping from the loading view + return; + } + + synchronized (mCache) { + // Cache the RemoteViews we loaded + mCache.insert(position, remoteViews, itemId); + + // Notify all the views that we have previously returned for this index that + // there is new data for it. + final RemoteViews rv = remoteViews; + final int typeId = mCache.getMetaDataAt(position).typeId; + mMainQueue.post(new Runnable() { + @Override + public void run() { + mRequestedViews.notifyOnRemoteViewsLoaded(position, rv, typeId); + } + }); + } + } } public Intent getRemoteViewsServiceIntent() { @@ -698,37 +701,132 @@ public class RemoteViewsAdapter extends BaseAdapter { public int getCount() { requestBindService(); - return mViewCache.getCount(); + final RemoteViewsMetaData metaData = mCache.getMetaData(); + synchronized (metaData) { + return metaData.count; + } } public Object getItem(int position) { - // disallow arbitrary object to be associated with an item for the time being + // Disallow arbitrary object to be associated with an item for the time being return null; } public long getItemId(int position) { requestBindService(); - return mViewCache.getItemId(position); + synchronized (mCache) { + if (mCache.containsMetaDataAt(position)) { + return mCache.getMetaDataAt(position).itemId; + } + return 0; + } } public int getItemViewType(int position) { requestBindService(); - return mViewCache.getItemViewType(position); + int typeId = 0; + synchronized (mCache) { + if (mCache.containsMetaDataAt(position)) { + typeId = mCache.getMetaDataAt(position).typeId; + } else { + return 0; + } + } + + final RemoteViewsMetaData metaData = mCache.getMetaData(); + synchronized (metaData) { + return metaData.getMappedViewType(typeId); + } + } + + /** + * Returns the item type id for the specified convert view. Returns -1 if the convert view + * is invalid. + */ + private int getConvertViewTypeId(View convertView) { + int typeId = -1; + if (convertView != null && convertView.getTag() != null) { + typeId = (Integer) convertView.getTag(); + } + return typeId; } public View getView(int position, View convertView, ViewGroup parent) { requestBindService(); - return mViewCache.getView(position, convertView, parent); + if (mServiceConnection.isConnected()) { + // "Request" an index so that we can queue it for loading, initiate subsequent + // preloading, etc. + synchronized (mCache) { + // Queue up other indices to be preloaded based on this position + mCache.queuePositionsToBePreloadedFromRequestedPosition(position); + + RemoteViewsFrameLayout layout = (RemoteViewsFrameLayout) convertView; + View convertViewChild = null; + int convertViewTypeId = 0; + if (convertView != null) { + convertViewChild = layout.getChildAt(0); + convertViewTypeId = getConvertViewTypeId(convertViewChild); + } + + // Second, we try and retrieve the RemoteViews from the cache, returning a loading + // view and queueing it to be loaded if it has not already been loaded. + if (mCache.containsRemoteViewAt(position)) { + Context context = parent.getContext(); + RemoteViews rv = mCache.getRemoteViewsAt(position); + int typeId = mCache.getMetaDataAt(position).typeId; + + // Reuse the convert view where possible + if (convertView != null) { + if (convertViewTypeId == typeId) { + rv.reapply(context, convertViewChild); + return convertView; + } + } + + // Otherwise, create a new view to be returned + View newView = rv.apply(context, parent); + newView.setTag(new Integer(typeId)); + if (convertView != null) { + layout.removeAllViews(); + } else { + layout = new RemoteViewsFrameLayout(context); + } + layout.addView(newView); + return layout; + } else { + // If the cache does not have the RemoteViews at this position, then create a + // loading view and queue the actual position to be loaded in the background + RemoteViewsFrameLayout loadingView = null; + final RemoteViewsMetaData metaData = mCache.getMetaData(); + synchronized (metaData) { + loadingView = metaData.createLoadingView(position, convertView, parent); + } + + mRequestedViews.add(position, loadingView); + mCache.queueRequestedPositionToLoad(position); + loadNextIndexInBackground(); + + return loadingView; + } + } + } + return new View(parent.getContext()); } public int getViewTypeCount() { requestBindService(); - return mViewCache.getViewTypeCount(); + final RemoteViewsMetaData metaData = mCache.getMetaData(); + synchronized (metaData) { + return metaData.viewTypeCount; + } } public boolean hasStableIds() { requestBindService(); - return mViewCache.hasStableIds(); + final RemoteViewsMetaData metaData = mCache.getMetaData(); + synchronized (metaData) { + return metaData.hasStableIds; + } } public boolean isEmpty() { @@ -736,14 +834,49 @@ public class RemoteViewsAdapter extends BaseAdapter { } public void notifyDataSetChanged() { - // flush the cache so that we can reload new items from the service - mViewCache.flushCache(); + synchronized (mCache) { + // Flush the cache so that we can reload new items from the service + mCache.reset(); + } + + final RemoteViewsMetaData metaData = mCache.getMetaData(); + synchronized (metaData) { + // Set flag to calls the remote factory's onDataSetChanged() on the next worker loop + metaData.isDataDirty = true; + } - // notify the factory that it's data may no longer be valid - mViewCache.onNotifyDataSetChanged(); + // Note: we do not call super.notifyDataSetChanged() until the RemoteViewsFactory has had + // a chance to update itself, and return new meta data associated with the new data. After + // which completeNotifyDataSetChanged() is called. } - public void completeNotifyDataSetChanged() { + private void completeNotifyDataSetChanged() { + // Complete the actual notifyDataSetChanged() call initiated earlier + if (mServiceConnection.isConnected()) { + IRemoteViewsFactory factory = mServiceConnection.getRemoteViewsFactory(); + try { + factory.onDataSetChanged(); + } catch (Exception e) { + Log.e(TAG, "Error in updateNotifyDataSetChanged(): " + e.getMessage()); + + // Return early to prevent from further being notified (since nothing has changed) + return; + } + } + + // Re-request the new metadata (only after the notification to the factory) + updateMetaData(); + + // Propagate the notification back to the base adapter + mMainQueue.post(new Runnable() { + @Override + public void run() { + superNotifyDataSetChanged(); + } + }); + } + + private void superNotifyDataSetChanged() { super.notifyDataSetChanged(); } @@ -755,10 +888,4 @@ public class RemoteViewsAdapter extends BaseAdapter { return mServiceConnection.isConnected(); } - - private void unbindService() { - if (mServiceConnection.isConnected()) { - mContext.unbindService(mServiceConnection); - } - } } diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index 4e61ddf..66149ac 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -67,7 +67,7 @@ public final class BatteryStatsImpl extends BatteryStats { private static final int MAGIC = 0xBA757475; // 'BATSTATS' // Current on-disk Parcel version - private static final int VERSION = 51; + private static final int VERSION = 52; // Maximum number of items we will record in the history. private static final int MAX_HISTORY_ITEMS = 2000; @@ -85,7 +85,7 @@ public final class BatteryStatsImpl extends BatteryStats { static final int MSG_UPDATE_WAKELOCKS = 1; static final int MSG_REPORT_POWER_CHANGE = 2; - static final long DELAY_UPDATE_WAKELOCKS = 15*1000; + static final long DELAY_UPDATE_WAKELOCKS = 5*1000; public interface BatteryCallback { public void batteryNeedsCpuUpdate(); @@ -1476,6 +1476,13 @@ public final class BatteryStatsImpl extends BatteryStats { } } + public void reportExcessiveCpuLocked(int uid, String proc, long overTime, long usedTime) { + Uid u = mUidStats.get(uid); + if (u != null) { + u.reportExcessiveCpuLocked(proc, overTime, usedTime); + } + } + int mSensorNesting; public void noteStartSensorLocked(int uid, int sensor) { @@ -2977,7 +2984,7 @@ public final class BatteryStatsImpl extends BatteryStats { SamplingCounter[] mSpeedBins; - ArrayList<ExcessiveWake> mExcessiveWake; + ArrayList<ExcessivePower> mExcessivePower; Proc() { mUnpluggables.add(this); @@ -3005,55 +3012,69 @@ public final class BatteryStatsImpl extends BatteryStats { } } - public int countExcessiveWakes() { - return mExcessiveWake != null ? mExcessiveWake.size() : 0; + public int countExcessivePowers() { + return mExcessivePower != null ? mExcessivePower.size() : 0; } - public ExcessiveWake getExcessiveWake(int i) { - if (mExcessiveWake != null) { - return mExcessiveWake.get(i); + public ExcessivePower getExcessivePower(int i) { + if (mExcessivePower != null) { + return mExcessivePower.get(i); } return null; } public void addExcessiveWake(long overTime, long usedTime) { - if (mExcessiveWake == null) { - mExcessiveWake = new ArrayList<ExcessiveWake>(); + if (mExcessivePower == null) { + mExcessivePower = new ArrayList<ExcessivePower>(); } - ExcessiveWake ew = new ExcessiveWake(); + ExcessivePower ew = new ExcessivePower(); + ew.type = ExcessivePower.TYPE_WAKE; ew.overTime = overTime; ew.usedTime = usedTime; - mExcessiveWake.add(ew); + mExcessivePower.add(ew); } - void writeExcessiveWakeToParcelLocked(Parcel out) { - if (mExcessiveWake == null) { + public void addExcessiveCpu(long overTime, long usedTime) { + if (mExcessivePower == null) { + mExcessivePower = new ArrayList<ExcessivePower>(); + } + ExcessivePower ew = new ExcessivePower(); + ew.type = ExcessivePower.TYPE_CPU; + ew.overTime = overTime; + ew.usedTime = usedTime; + mExcessivePower.add(ew); + } + + void writeExcessivePowerToParcelLocked(Parcel out) { + if (mExcessivePower == null) { out.writeInt(0); return; } - final int N = mExcessiveWake.size(); + final int N = mExcessivePower.size(); out.writeInt(N); for (int i=0; i<N; i++) { - ExcessiveWake ew = mExcessiveWake.get(i); + ExcessivePower ew = mExcessivePower.get(i); + out.writeInt(ew.type); out.writeLong(ew.overTime); out.writeLong(ew.usedTime); } } - void readExcessiveWakeFromParcelLocked(Parcel in) { + void readExcessivePowerFromParcelLocked(Parcel in) { final int N = in.readInt(); if (N == 0) { - mExcessiveWake = null; + mExcessivePower = null; return; } - mExcessiveWake = new ArrayList<ExcessiveWake>(); + mExcessivePower = new ArrayList<ExcessivePower>(); for (int i=0; i<N; i++) { - ExcessiveWake ew = new ExcessiveWake(); + ExcessivePower ew = new ExcessivePower(); + ew.type = in.readInt(); ew.overTime = in.readLong(); ew.usedTime = in.readLong(); - mExcessiveWake.add(ew); + mExcessivePower.add(ew); } } @@ -3082,7 +3103,7 @@ public final class BatteryStatsImpl extends BatteryStats { } } - writeExcessiveWakeToParcelLocked(out); + writeExcessivePowerToParcelLocked(out); } void readFromParcelLocked(Parcel in) { @@ -3112,7 +3133,7 @@ public final class BatteryStatsImpl extends BatteryStats { } } - readExcessiveWakeFromParcelLocked(in); + readExcessivePowerFromParcelLocked(in); } public BatteryStatsImpl getBatteryStats() { @@ -3746,6 +3767,13 @@ public final class BatteryStatsImpl extends BatteryStats { } } + public void reportExcessiveCpuLocked(String proc, long overTime, long usedTime) { + Proc p = getProcessStatsLocked(proc); + if (p != null) { + p.addExcessiveCpu(overTime, usedTime); + } + } + public void noteStartSensor(int sensor) { StopwatchTimer t = getSensorTimerLocked(sensor, true); if (t != null) { @@ -4688,7 +4716,7 @@ public final class BatteryStatsImpl extends BatteryStats { p.mSpeedBins[i].readSummaryFromParcelLocked(in); } } - p.readExcessiveWakeFromParcelLocked(in); + p.readExcessivePowerFromParcelLocked(in); } NP = in.readInt(); @@ -4887,7 +4915,7 @@ public final class BatteryStatsImpl extends BatteryStats { out.writeInt(0); } } - ps.writeExcessiveWakeToParcelLocked(out); + ps.writeExcessivePowerToParcelLocked(out); } } diff --git a/core/java/com/android/internal/widget/DrawableHolder.java b/core/java/com/android/internal/widget/DrawableHolder.java new file mode 100644 index 0000000..d53860c --- /dev/null +++ b/core/java/com/android/internal/widget/DrawableHolder.java @@ -0,0 +1,232 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.widget; + +import java.util.ArrayList; + +import android.animation.Animator; +import android.animation.ObjectAnimator; +import android.animation.ValueAnimator; +import android.animation.Animator.AnimatorListener; +import android.graphics.Canvas; +import android.graphics.drawable.BitmapDrawable; +import android.util.Log; +import android.view.animation.DecelerateInterpolator; + +/** + * This class is a container for a Drawable with multiple animated properties. + * + */ +public class DrawableHolder implements AnimatorListener { + public static final DecelerateInterpolator EASE_OUT_INTERPOLATOR = new DecelerateInterpolator(); + private static final String TAG = "DrawableHolder"; + private static final boolean DBG = false; + private float mX = 0.0f; + private float mY = 0.0f; + private float mScaleX = 1.0f; + private float mScaleY = 1.0f; + private BitmapDrawable mDrawable; + private float mAlpha = 1f; + private ArrayList<ObjectAnimator<Float>> mAnimators = new ArrayList<ObjectAnimator<Float>>(); + private ArrayList<ObjectAnimator<Float>> mNeedToStart = new ArrayList<ObjectAnimator<Float>>(); + + public DrawableHolder(BitmapDrawable drawable) { + this(drawable, 0.0f, 0.0f); + } + + public DrawableHolder(BitmapDrawable drawable, float x, float y) { + mDrawable = drawable; + mX = x; + mY = y; + mDrawable.getPaint().setAntiAlias(true); // Force AA + mDrawable.setBounds(0, 0, mDrawable.getIntrinsicWidth(), mDrawable.getIntrinsicHeight()); + } + + /** + * + * Adds an animation that interpolates given property from its current value + * to the given value. + * + * @param duration the duration, in ms. + * @param delay the delay to start the animation, in ms. + * @param property the property to animate + * @param toValue the target value + * @param replace if true, replace the current animation with this one. + */ + public ObjectAnimator<Float> addAnimTo(long duration, long delay, + String property, float toValue, boolean replace) { + + if (replace) removeAnimationFor(property); + + ObjectAnimator<Float> anim = new ObjectAnimator<Float>(duration, this, property, toValue); + anim.setStartDelay(delay); + anim.setInterpolator(EASE_OUT_INTERPOLATOR); + this.addAnimation(anim, replace); + if (DBG) Log.v(TAG, "animationCount = " + mAnimators.size()); + return anim; + } + + /** + * Stops all animations for the given property and removes it from the list. + * + * @param property + */ + public void removeAnimationFor(String property) { + ArrayList<ObjectAnimator<Float>> removalList = new ArrayList<ObjectAnimator<Float>>(); + for (ObjectAnimator<Float> currentAnim : mAnimators) { + if (property.equals(currentAnim.getPropertyName())) { + currentAnim.cancel(); + removalList.add(currentAnim); + } + } + if (DBG) Log.v(TAG, "Remove list size: " + removalList.size()); + mAnimators.removeAll(removalList); + } + + /** + * Stops all animations and removes them from the list. + */ + public void clearAnimations() { + for (ObjectAnimator<Float> currentAnim : mAnimators) { + currentAnim.cancel(); + } + mAnimators.clear(); + } + + /** + * Adds the given animation to the list of animations for this object. + * + * @param anim + * @param overwrite + * @return + */ + private DrawableHolder addAnimation(ObjectAnimator<Float> anim, boolean overwrite) { + if (anim != null) + mAnimators.add(anim); + mNeedToStart.add(anim); + return this; + } + + /** + * Draw this object to the canvas using the properties defined in this class. + * + * @param canvas canvas to draw into + */ + public void draw(Canvas canvas) { + final float threshold = 1.0f / 256.0f; // contribution less than 1 LSB of RGB byte + if (mAlpha <= threshold) // don't bother if it won't show up + return; + canvas.save(Canvas.MATRIX_SAVE_FLAG); + canvas.translate(mX, mY); + canvas.scale(mScaleX, mScaleY); + canvas.translate(-0.5f*getWidth(), -0.5f*getHeight()); + mDrawable.setAlpha((int) Math.round(mAlpha * 255f)); + mDrawable.draw(canvas); + canvas.restore(); + } + + /** + * Starts all animations added since the last call to this function. Used to synchronize + * animations. + * + * @param listener an optional listener to add to the animations. Typically used to know when + * to invalidate the surface these are being drawn to. + */ + public void startAnimations(ValueAnimator.AnimatorUpdateListener listener) { + for (int i = 0; i < mNeedToStart.size(); i++) { + ObjectAnimator<Float> anim = mNeedToStart.get(i); + anim.addUpdateListener(listener); + anim.addListener(this); + anim.start(); + } + mNeedToStart.clear(); + } + + + public DrawableHolder setX(float value) { + mX = value; + return this; + } + + public DrawableHolder setY(float value) { + mY = value; + return this; + } + + public DrawableHolder setScaleX(float value) { + mScaleX = value; + return this; + } + + public DrawableHolder setScaleY(float value) { + mScaleY = value; + return this; + } + + public DrawableHolder setAlpha(float alpha) { + mAlpha = alpha; + return this; + } + + public float getX() { + return mX; + } + + public float getY() { + return mY; + } + + public float getScaleX() { + return mScaleX; + } + + public float getScaleY() { + return mScaleY; + } + + public float getAlpha() { + return mAlpha; + } + + public BitmapDrawable getDrawable() { + return mDrawable; + } + + public int getWidth() { + return mDrawable.getIntrinsicWidth(); + } + + public int getHeight() { + return mDrawable.getIntrinsicHeight(); + } + + public void onAnimationCancel(Animator animation) { + + } + + public void onAnimationEnd(Animator animation) { + mAnimators.remove(animation); + } + + public void onAnimationRepeat(Animator animation) { + + } + + public void onAnimationStart(Animator animation) { + + } +} diff --git a/core/java/com/android/internal/widget/WaveView.java b/core/java/com/android/internal/widget/WaveView.java new file mode 100644 index 0000000..f4ee7ee --- /dev/null +++ b/core/java/com/android/internal/widget/WaveView.java @@ -0,0 +1,573 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.widget; + +import java.util.ArrayList; + +import android.animation.ValueAnimator; +import android.content.Context; +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Canvas; +import android.graphics.drawable.BitmapDrawable; +import android.os.Vibrator; +import android.util.AttributeSet; +import android.util.Log; +import android.view.MotionEvent; +import android.view.View; + +import com.android.internal.R; + +/** + * A special widget containing a center and outer ring. Moving the center ring to the outer ring + * causes an event that can be caught by implementing OnTriggerListener. + */ +public class WaveView extends View implements ValueAnimator.AnimatorUpdateListener { + private static final String TAG = "WaveView"; + private static final boolean DBG = false; + private static final int WAVE_COUNT = 5; // default wave count + private static final long VIBRATE_SHORT = 20; // msec + private static final long VIBRATE_LONG = 20; // msec + + // Lock state machine states + private static final int STATE_RESET_LOCK = 0; + private static final int STATE_READY = 1; + private static final int STATE_START_ATTEMPT = 2; + private static final int STATE_ATTEMPTING = 3; + private static final int STATE_UNLOCK_ATTEMPT = 4; + private static final int STATE_UNLOCK_SUCCESS = 5; + + // Animation properties. + private static final long DURATION = 500; // duration of transitional animations + private static final long FINAL_DELAY = 1300; // delay for final animations + private static final long SHORT_DELAY = 100; // for starting one animation after another. + private static final long WAVE_DURATION = 2000; // amount of time for way to expand/decay + private static final long RESET_TIMEOUT = 3000; // elapsed time of inactivity before we reset + private static final long DELAY_INCREMENT = 15; // increment per wave while tracking motion + private static final long DELAY_INCREMENT2 = 12; // increment per wave while not tracking + + private Vibrator mVibrator; + private OnTriggerListener mOnTriggerListener; + private ArrayList<DrawableHolder> mDrawables = new ArrayList<DrawableHolder>(3); + private ArrayList<DrawableHolder> mLightWaves = new ArrayList<DrawableHolder>(WAVE_COUNT); + private boolean mFingerDown = false; + private float mRingRadius = 182.0f; // Radius of bitmap ring. Used to snap halo to it + private int mSnapRadius = 136; // minimum threshold for drag unlock + private int mWaveDelay = 240; // time to delay + private int mWaveCount = WAVE_COUNT; // number of waves + private long mWaveTimerDelay = mWaveDelay; + private int mCurrentWave = 0; + private float mLockCenterX; // center of widget as dictated by widget size + private float mLockCenterY; + private float mMouseX; // current mouse position as of last touch event + private float mMouseY; + private DrawableHolder mUnlockRing; + private DrawableHolder mUnlockDefault; + private DrawableHolder mUnlockHalo; + private int mLockState = STATE_RESET_LOCK; + private int mGrabbedState = OnTriggerListener.NO_HANDLE; + + public WaveView(Context context) { + this(context, null); + } + + public WaveView(Context context, AttributeSet attrs) { + super(context, attrs); + + // TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.WaveView); + // mOrientation = a.getInt(R.styleable.WaveView_orientation, HORIZONTAL); + // a.recycle(); + + initDrawables(); + } + + @Override + protected void onSizeChanged(int w, int h, int oldw, int oldh) { + mLockCenterX = 0.5f * w; + mLockCenterY = 0.5f * h; + super.onSizeChanged(w, h, oldw, oldh); + } + + @Override + protected int getSuggestedMinimumWidth() { + // View should be large enough to contain the unlock ring + halo + return mUnlockRing.getWidth() + mUnlockHalo.getWidth(); + } + + @Override + protected int getSuggestedMinimumHeight() { + // View should be large enough to contain the unlock ring + halo + return mUnlockRing.getHeight() + mUnlockHalo.getHeight(); + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec); + int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec); + int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec); + int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec); + int width; + int height; + + if (widthSpecMode == MeasureSpec.AT_MOST) { + width = Math.min(widthSpecSize, getSuggestedMinimumWidth()); + } else if (widthSpecMode == MeasureSpec.EXACTLY) { + width = widthSpecSize; + } else { + width = getSuggestedMinimumWidth(); + } + + if (heightSpecMode == MeasureSpec.AT_MOST) { + height = Math.min(heightSpecSize, getSuggestedMinimumWidth()); + } else if (heightSpecMode == MeasureSpec.EXACTLY) { + height = heightSpecSize; + } else { + height = getSuggestedMinimumHeight(); + } + + setMeasuredDimension(width, height); + } + + private void initDrawables() { + mUnlockRing = new DrawableHolder(createDrawable(R.drawable.unlock_ring)) + .setX(mLockCenterX).setY(mLockCenterY).setScaleX(0.1f).setScaleY(0.1f).setAlpha(0.0f); + mDrawables.add(mUnlockRing); + + mUnlockDefault = new DrawableHolder(createDrawable(R.drawable.unlock_default)) + .setX(mLockCenterX).setY(mLockCenterY).setScaleX(0.1f).setScaleY(0.1f).setAlpha(0.0f); + mDrawables.add(mUnlockDefault); + + mUnlockHalo = new DrawableHolder(createDrawable(R.drawable.unlock_halo)) + .setX(mLockCenterX).setY(mLockCenterY).setScaleX(0.1f).setScaleY(0.1f).setAlpha(0.0f); + mDrawables.add(mUnlockHalo); + + BitmapDrawable wave = createDrawable(R.drawable.unlock_wave); + for (int i = 0; i < mWaveCount; i++) { + DrawableHolder holder = new DrawableHolder(wave); + mLightWaves.add(holder); + holder.setAlpha(0.0f); + } + } + + private void waveUpdateFrame(float mouseX, float mouseY, boolean fingerDown) { + double distX = mouseX - mLockCenterX; + double distY = mouseY - mLockCenterY; + int dragDistance = (int) Math.ceil(Math.hypot(distX, distY)); + double touchA = Math.atan2(distX, distY); + float ringX = (float) (mLockCenterX + mRingRadius * Math.sin(touchA)); + float ringY = (float) (mLockCenterY + mRingRadius * Math.cos(touchA)); + + switch (mLockState) { + case STATE_RESET_LOCK: + if (DBG) Log.v(TAG, "State RESET_LOCK"); + mWaveTimerDelay = mWaveDelay; + for (int i = 0; i < mLightWaves.size(); i++) { + //TweenMax.to(mLightWave.get(i), .3, {alpha:0, ease:Quint.easeOut}); + DrawableHolder holder = mLightWaves.get(i); + holder.addAnimTo(300, 0, "alpha", 0.0f, false); + } + for (int i = 0; i < mLightWaves.size(); i++) { + mLightWaves.get(i).startAnimations(this); + } + + //TweenMax.to(unlockRing, .5, { x: lockX, y: lockY, scaleX: .1, scaleY: .1, + // alpha: 0, overwrite: true, ease:Quint.easeOut }); + mUnlockRing.addAnimTo(DURATION, 0, "x", mLockCenterX, true); + mUnlockRing.addAnimTo(DURATION, 0, "y", mLockCenterY, true); + mUnlockRing.addAnimTo(DURATION, 0, "scaleX", 0.1f, true); + mUnlockRing.addAnimTo(DURATION, 0, "scaleY", 0.1f, true); + mUnlockRing.addAnimTo(DURATION, 0, "alpha", 0.0f, true); + + //TweenMax.to(unlockDefault, 0, { x: lockX, y: lockY, scaleX: .1, scaleY: .1, + // alpha: 0 , overwrite: true }); + mUnlockDefault.removeAnimationFor("x"); + mUnlockDefault.removeAnimationFor("y"); + mUnlockDefault.removeAnimationFor("scaleX"); + mUnlockDefault.removeAnimationFor("scaleY"); + mUnlockDefault.removeAnimationFor("alpha"); + mUnlockDefault.setX(mLockCenterX).setY(mLockCenterY).setScaleX(0.1f).setScaleY(0.1f) + .setAlpha(0.0f); + + //TweenMax.to(unlockDefault, .5, { delay: .1, scaleX: 1, scaleY: 1, + // alpha: 1, overwrite: true, ease:Quint.easeOut }); + mUnlockDefault.addAnimTo(DURATION, SHORT_DELAY, "scaleX", 1.0f, true); + mUnlockDefault.addAnimTo(DURATION, SHORT_DELAY, "scaleY", 1.0f, true); + mUnlockDefault.addAnimTo(DURATION, SHORT_DELAY, "alpha", 1.0f, true); + + //TweenMax.to(unlockHalo, 0, { x: lockX, y: lockY, scaleX:.1, scaleY: .1, + // alpha: 0 , overwrite: true }); + mUnlockHalo.removeAnimationFor("x"); + mUnlockHalo.removeAnimationFor("y"); + mUnlockHalo.removeAnimationFor("scaleX"); + mUnlockHalo.removeAnimationFor("scaleY"); + mUnlockHalo.removeAnimationFor("alpha"); + mUnlockHalo.setX(mLockCenterX).setY(mLockCenterY).setScaleX(0.1f).setScaleY(0.1f) + .setAlpha(0.0f); + + //TweenMax.to(unlockHalo, .5, { x: lockX, y: lockY, scaleX: 1, scaleY: 1, + // alpha: 1 , overwrite: true, ease:Quint.easeOut }); + mUnlockHalo.addAnimTo(DURATION, SHORT_DELAY, "x", mLockCenterX, true); + mUnlockHalo.addAnimTo(DURATION, SHORT_DELAY, "y", mLockCenterY, true); + mUnlockHalo.addAnimTo(DURATION, SHORT_DELAY, "scaleX", 1.0f, true); + mUnlockHalo.addAnimTo(DURATION, SHORT_DELAY, "scaleY", 1.0f, true); + mUnlockHalo.addAnimTo(DURATION, SHORT_DELAY, "alpha", 1.0f, true); + + //lockTimer.stop(); + removeCallbacks(mLockTimerActions); + + mLockState = STATE_READY; + break; + + case STATE_READY: + if (DBG) Log.v(TAG, "State READY"); + break; + + case STATE_START_ATTEMPT: + if (DBG) Log.v(TAG, "State START_ATTEMPT"); + //TweenMax.to(unlockDefault, 0, {scaleX: .1, scaleY:.1, alpha: 0, + // x:lockX +182, y: lockY , overwrite: true }); + mUnlockDefault.removeAnimationFor("x"); + mUnlockDefault.removeAnimationFor("y"); + mUnlockDefault.removeAnimationFor("scaleX"); + mUnlockDefault.removeAnimationFor("scaleY"); + mUnlockDefault.removeAnimationFor("alpha"); + mUnlockDefault.setX(mLockCenterX + 182).setY(mLockCenterY).setScaleX(0.1f) + .setScaleY(0.1f).setAlpha(0.0f); + + //TweenMax.to(unlockDefault, 0.5, { delay: .1 , scaleX: 1, scaleY: 1, + // alpha: 1, ease:Quint.easeOut }); + mUnlockDefault.addAnimTo(DURATION, SHORT_DELAY, "scaleX", 1.0f, false); + mUnlockDefault.addAnimTo(DURATION, SHORT_DELAY, "scaleY", 1.0f, false); + mUnlockDefault.addAnimTo(DURATION, SHORT_DELAY, "alpha", 1.0f, false); + + //TweenMax.to(unlockRing, 0.5, {scaleX: 1, scaleY: 1, + // alpha: 1, ease:Quint.easeOut, overwrite: true }); + mUnlockRing.addAnimTo(DURATION, 0, "scaleX", 1.0f, true); + mUnlockRing.addAnimTo(DURATION, 0, "scaleY", 1.0f, true); + mUnlockRing.addAnimTo(DURATION, 0, "alpha", 1.0f, true); + + postDelayed(mAddWaveAction, mWaveTimerDelay); + + mLockState = STATE_ATTEMPTING; + break; + + case STATE_ATTEMPTING: + if (DBG) Log.v(TAG, "State ATTEMPTING"); + //TweenMax.to(unlockHalo, 0.4, { x:mouseX, y:mouseY, scaleX:1, scaleY:1, + // alpha: 1, ease:Quint.easeOut }); + if (dragDistance > mSnapRadius) { + if (fingerDown) { + //TweenMax.to(unlockHalo, 0.4, {x:ringX, y:ringY, scaleX:1, scaleY:1, + // alpha: 1 , ease:Quint.easeOut , overwrite: true }); + mUnlockHalo.addAnimTo(0, 0, "x", ringX, true); + mUnlockHalo.addAnimTo(0, 0, "y", ringY, true); + mUnlockHalo.addAnimTo(0, 0, "scaleX", 1.0f, true); + mUnlockHalo.addAnimTo(0, 0, "scaleY", 1.0f, true); + mUnlockHalo.addAnimTo(0, 0, "alpha", 1.0f, true); + } else { + mLockState = STATE_UNLOCK_ATTEMPT; + } + } else { + mUnlockHalo.addAnimTo(0, 0, "x", mouseX, true); + mUnlockHalo.addAnimTo(0, 0, "y", mouseY, true); + mUnlockHalo.addAnimTo(0, 0, "scaleX", 1.0f, true); + mUnlockHalo.addAnimTo(0, 0, "scaleY", 1.0f, true); + mUnlockHalo.addAnimTo(0, 0, "alpha", 1.0f, true); + } + break; + + case STATE_UNLOCK_ATTEMPT: + if (DBG) Log.v(TAG, "State UNLOCK_ATTEMPT"); + if (dragDistance > mSnapRadius) { + for (int n = 0; n < mLightWaves.size(); n++) { + //TweenMax.to(this["lightWave"+n], .5,{alpha:0, delay: (6+n-currentWave)*.1, + // x:ringX, y:ringY, scaleX: .1, scaleY: .1, ease:Quint.easeOut}); + DrawableHolder wave = mLightWaves.get(n); + long delay = 1000L*(6 + n - mCurrentWave)/10L; + wave.addAnimTo(DURATION, delay, "x", ringX, true); + wave.addAnimTo(DURATION, delay, "y", ringY, true); + wave.addAnimTo(DURATION, delay, "scaleX", 0.1f, true); + wave.addAnimTo(DURATION, delay, "scaleY", 0.1f, true); + wave.addAnimTo(DURATION, delay, "alpha", 0.0f, true); + } + for (int i = 0; i < mLightWaves.size(); i++) { + mLightWaves.get(i).startAnimations(this); + } + + //TweenMax.to(unlockRing, .5, {x:ringX, y: ringY, scaleX: .1, scaleY: .1, + // alpha: 0, ease: Quint.easeOut }); + mUnlockRing.addAnimTo(DURATION, 0, "x", ringX, false); + mUnlockRing.addAnimTo(DURATION, 0, "y", ringY, false); + mUnlockRing.addAnimTo(DURATION, 0, "scaleX", 0.1f, false); + mUnlockRing.addAnimTo(DURATION, 0, "scaleY", 0.1f, false); + mUnlockRing.addAnimTo(DURATION, 0, "alpha", 0.0f, false); + + //TweenMax.to(unlockRing, .5, { delay: 1.3, alpha: 0 , ease: Quint.easeOut }); + mUnlockRing.addAnimTo(DURATION, FINAL_DELAY, "alpha", 0.0f, false); + + //TweenMax.to(unlockDefault, 0, { x:ringX, y: ringY, scaleX: .1, scaleY: .1, + // alpha: 0 , overwrite: true }); + mUnlockDefault.removeAnimationFor("x"); + mUnlockDefault.removeAnimationFor("y"); + mUnlockDefault.removeAnimationFor("scaleX"); + mUnlockDefault.removeAnimationFor("scaleY"); + mUnlockDefault.removeAnimationFor("alpha"); + mUnlockDefault.setX(ringX).setY(ringY).setScaleX(0.1f).setScaleY(0.1f) + .setAlpha(0.0f); + + //TweenMax.to(unlockDefault, .5, { x:ringX, y: ringY, scaleX: 1, scaleY: 1, + // alpha: 1 , ease: Quint.easeOut , overwrite: true }); + mUnlockDefault.addAnimTo(DURATION, 0, "x", ringX, true); + mUnlockDefault.addAnimTo(DURATION, 0, "y", ringY, true); + mUnlockDefault.addAnimTo(DURATION, 0, "scaleX", 1.0f, true); + mUnlockDefault.addAnimTo(DURATION, 0, "scaleY", 1.0f, true); + mUnlockDefault.addAnimTo(DURATION, 0, "alpha", 1.0f, true); + + //TweenMax.to(unlockDefault, .5, { delay: 1.3, scaleX: 3, scaleY: 3, + // alpha: 1, ease: Quint.easeOut }); + mUnlockDefault.addAnimTo(DURATION, FINAL_DELAY, "scaleX", 3.0f, false); + mUnlockDefault.addAnimTo(DURATION, FINAL_DELAY, "scaleY", 3.0f, false); + mUnlockDefault.addAnimTo(DURATION, FINAL_DELAY, "alpha", 1.0f, false); + + //TweenMax.to(unlockHalo, .5, { x:ringX, y: ringY , ease: Back.easeOut }); + mUnlockHalo.addAnimTo(DURATION, 0, "x", ringX, false); + mUnlockHalo.addAnimTo(DURATION, 0, "y", ringY, false); + + //TweenMax.to(unlockHalo, .5, { delay: 1.3, scaleX: 3, scaleY: 3, + // alpha: 1, ease: Quint.easeOut }); + mUnlockHalo.addAnimTo(DURATION, FINAL_DELAY, "scaleX", 3.0f, false); + mUnlockHalo.addAnimTo(DURATION, FINAL_DELAY, "scaleY", 3.0f, false); + mUnlockHalo.addAnimTo(DURATION, FINAL_DELAY, "alpha", 1.0f, false); + + removeCallbacks(mLockTimerActions); + + postDelayed(mLockTimerActions, RESET_TIMEOUT); + + dispatchTriggerEvent(OnTriggerListener.CENTER_HANDLE); + mLockState = STATE_UNLOCK_SUCCESS; + } else { + mLockState = STATE_RESET_LOCK; + } + break; + + case STATE_UNLOCK_SUCCESS: + if (DBG) Log.v(TAG, "State UNLOCK_SUCCESS"); + removeCallbacks(mAddWaveAction); + break; + + default: + if (DBG) Log.v(TAG, "Unknown state " + mLockState); + break; + } + mUnlockDefault.startAnimations(this); + mUnlockHalo.startAnimations(this); + mUnlockRing.startAnimations(this); + } + + BitmapDrawable createDrawable(int resId) { + Resources res = getResources(); + Bitmap bitmap = BitmapFactory.decodeResource(res, resId); + return new BitmapDrawable(res, bitmap); + } + + @Override + protected void onDraw(Canvas canvas) { + waveUpdateFrame(mMouseX, mMouseY, mFingerDown); + for (int i = 0; i < mDrawables.size(); ++i) { + mDrawables.get(i).draw(canvas); + } + for (int i = 0; i < mLightWaves.size(); ++i) { + mLightWaves.get(i).draw(canvas); + } + } + + private final Runnable mLockTimerActions = new Runnable() { + public void run() { + if (DBG) Log.v(TAG, "LockTimerActions"); + // reset lock after inactivity + if (mLockState == STATE_ATTEMPTING) { + mLockState = STATE_RESET_LOCK; + } + // for prototype, reset after successful unlock + if (mLockState == STATE_UNLOCK_SUCCESS) { + mLockState = STATE_RESET_LOCK; + } + invalidate(); + } + }; + + private final Runnable mAddWaveAction = new Runnable() { + public void run() { + double distX = mMouseX - mLockCenterX; + double distY = mMouseY - mLockCenterY; + int dragDistance = (int) Math.ceil(Math.hypot(distX, distY)); + if (mLockState == STATE_ATTEMPTING && dragDistance < mSnapRadius + && mWaveTimerDelay >= mWaveDelay) { + mWaveTimerDelay = Math.min(WAVE_DURATION, mWaveTimerDelay + DELAY_INCREMENT); + + DrawableHolder wave = mLightWaves.get(mCurrentWave); + wave.setAlpha(0.0f); + wave.setScaleX(0.2f); + wave.setScaleY(0.2f); + wave.setX(mMouseX); + wave.setY(mMouseY); + + //TweenMax.to(this["lightWave"+currentWave], 2, { x:lockX , y:lockY, alpha: 1.5, + // scaleX: 1, scaleY:1, ease:Cubic.easeOut}); + wave.addAnimTo(WAVE_DURATION, 0, "x", mLockCenterX, true); + wave.addAnimTo(WAVE_DURATION, 0, "y", mLockCenterY, true); + wave.addAnimTo(WAVE_DURATION*2/3, 0, "alpha", 1.0f, true); + wave.addAnimTo(WAVE_DURATION, 0, "scaleX", 1.0f, true); + wave.addAnimTo(WAVE_DURATION, 0, "scaleY", 1.0f, true); + + //TweenMax.to(this["lightWave"+currentWave], 1, { delay: 1.3 + // , alpha: 0 , ease:Quint.easeOut}); + wave.addAnimTo(1000, FINAL_DELAY, "alpha", 0.0f, false); + wave.startAnimations(WaveView.this); + + mCurrentWave = (mCurrentWave+1) % mWaveCount; + if (DBG) Log.v(TAG, "WaveTimerDelay: start new wave in " + mWaveTimerDelay); + postDelayed(mAddWaveAction, mWaveTimerDelay); + } else { + mWaveTimerDelay += DELAY_INCREMENT2; + } + } + }; + + @Override + public boolean onTouchEvent(MotionEvent event) { + final int action = event.getAction(); + mMouseX = event.getX(); + mMouseY = event.getY(); + boolean handled = false; + switch (action) { + case MotionEvent.ACTION_DOWN: + removeCallbacks(mLockTimerActions); + mFingerDown = true; + setGrabbedState(OnTriggerListener.CENTER_HANDLE); + { + float x = mMouseX - mUnlockHalo.getX(); + float y = mMouseY - mUnlockHalo.getY(); + float dist = (float) Math.hypot(x, y); + if (dist < mUnlockHalo.getWidth()*0.5f) { + if (mLockState == STATE_READY) { + mLockState = STATE_START_ATTEMPT; + } + } + } + handled = true; + break; + + case MotionEvent.ACTION_MOVE: + handled = true; + break; + + case MotionEvent.ACTION_UP: + mFingerDown = false; + postDelayed(mLockTimerActions, RESET_TIMEOUT); + setGrabbedState(OnTriggerListener.NO_HANDLE); + handled = true; + break; + + case MotionEvent.ACTION_CANCEL: + mFingerDown = false; + handled = true; + break; + } + invalidate(); + return handled ? true : super.onTouchEvent(event); + } + + /** + * Triggers haptic feedback. + */ + private synchronized void vibrate(long duration) { + if (mVibrator == null) { + mVibrator = (android.os.Vibrator) + getContext().getSystemService(Context.VIBRATOR_SERVICE); + } + mVibrator.vibrate(duration); + } + + /** + * Registers a callback to be invoked when the user triggers an event. + * + * @param listener the OnDialTriggerListener to attach to this view + */ + public void setOnTriggerListener(OnTriggerListener listener) { + mOnTriggerListener = listener; + } + + /** + * Dispatches a trigger event to listener. Ignored if a listener is not set. + * @param whichHandle the handle that triggered the event. + */ + private void dispatchTriggerEvent(int whichHandle) { + vibrate(VIBRATE_LONG); + if (mOnTriggerListener != null) { + mOnTriggerListener.onTrigger(this, whichHandle); + } + } + + /** + * Sets the current grabbed state, and dispatches a grabbed state change + * event to our listener. + */ + private void setGrabbedState(int newState) { + if (newState != mGrabbedState) { + mGrabbedState = newState; + if (mOnTriggerListener != null) { + mOnTriggerListener.onGrabbedStateChange(this, mGrabbedState); + } + } + } + + public interface OnTriggerListener { + /** + * Sent when the user releases the handle. + */ + public static final int NO_HANDLE = 0; + + /** + * Sent when the user grabs the center handle + */ + public static final int CENTER_HANDLE = 10; + + /** + * Called when the user drags the center ring beyond a threshold. + */ + void onTrigger(View v, int whichHandle); + + /** + * Called when the "grabbed state" changes (i.e. when the user either grabs or releases + * one of the handles.) + * + * @param v the view that was triggered + * @param grabbedState the new state: {@link #NO_HANDLE}, {@link #CENTER_HANDLE}, + */ + void onGrabbedStateChange(View v, int grabbedState); + } + + public void onAnimationUpdate(ValueAnimator animation) { + invalidate(); + } + + public void reset() { + mLockState = STATE_RESET_LOCK; + } +} diff --git a/core/jni/android_media_AudioTrack.cpp b/core/jni/android_media_AudioTrack.cpp index 9d215b7..8409adc 100644 --- a/core/jni/android_media_AudioTrack.cpp +++ b/core/jni/android_media_AudioTrack.cpp @@ -252,21 +252,23 @@ android_media_AudioTrack_native_setup(JNIEnv *env, jobject thiz, jobject weak_th lpJniStorage->mCallbackData.audioTrack_ref = env->NewGlobalRef(weak_this); lpJniStorage->mStreamType = atStreamType; - - jint* nSession = NULL; - if (jSession) { - nSession = (jint *) env->GetPrimitiveArrayCritical(jSession, NULL); - if (nSession == NULL) { - LOGE("Error creating AudioTrack: Error retrieving session id pointer"); - delete lpJniStorage; - return AUDIOTRACK_ERROR; - } - } else { + + if (jSession == NULL) { LOGE("Error creating AudioTrack: invalid session ID pointer"); delete lpJniStorage; return AUDIOTRACK_ERROR; } + jint* nSession = (jint *) env->GetPrimitiveArrayCritical(jSession, NULL); + if (nSession == NULL) { + LOGE("Error creating AudioTrack: Error retrieving session id pointer"); + delete lpJniStorage; + return AUDIOTRACK_ERROR; + } + int sessionId = nSession[0]; + env->ReleasePrimitiveArrayCritical(jSession, nSession, 0); + nSession = NULL; + // create the native AudioTrack object AudioTrack* lpTrack = new AudioTrack(); if (lpTrack == NULL) { @@ -288,7 +290,7 @@ android_media_AudioTrack_native_setup(JNIEnv *env, jobject thiz, jobject weak_th 0,// notificationFrames == 0 since not using EVENT_MORE_DATA to feed the AudioTrack 0,// shared mem true,// thread can call Java - nSession[0]);// audio session ID + sessionId);// audio session ID } else if (memoryMode == javaAudioTrackFields.MODE_STATIC) { // AudioTrack is using shared memory @@ -309,7 +311,7 @@ android_media_AudioTrack_native_setup(JNIEnv *env, jobject thiz, jobject weak_th 0,// notificationFrames == 0 since not using EVENT_MORE_DATA to feed the AudioTrack lpJniStorage->mMemBase,// shared mem true,// thread can call Java - nSession[0]);// audio session ID + sessionId);// audio session ID } if (lpTrack->initCheck() != NO_ERROR) { @@ -317,9 +319,13 @@ android_media_AudioTrack_native_setup(JNIEnv *env, jobject thiz, jobject weak_th goto native_init_failure; } + nSession = (jint *) env->GetPrimitiveArrayCritical(jSession, NULL); + if (nSession == NULL) { + LOGE("Error creating AudioTrack: Error retrieving session id pointer"); + goto native_init_failure; + } // read the audio session ID back from AudioTrack in case we create a new session nSession[0] = lpTrack->getSessionId(); - env->ReleasePrimitiveArrayCritical(jSession, nSession, 0); nSession = NULL; diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 06c8423..d5065f6 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -244,6 +244,14 @@ android:description="@string/permdesc_writeHistoryBookmarks" android:protectionLevel="dangerous" /> + <!-- Allows an application to broadcast an Intent to set an alarm for the + user. --> + <permission android:name="com.android.alarm.permission.SET_ALARM" + android:permissionGroup="android.permission-group.PERSONAL_INFO" + android:label="@string/permlab_setAlarm" + android:description="@string/permdesc_setAlarm" + android:protectionLevel="normal" /> + <!-- ======================================= --> <!-- Permissions for accessing location info --> <!-- ======================================= --> diff --git a/core/res/res/drawable-xlarge/unlock_default.png b/core/res/res/drawable-xlarge/unlock_default.png Binary files differnew file mode 100644 index 0000000..0a441c0 --- /dev/null +++ b/core/res/res/drawable-xlarge/unlock_default.png diff --git a/core/res/res/drawable-xlarge/unlock_halo.png b/core/res/res/drawable-xlarge/unlock_halo.png Binary files differnew file mode 100644 index 0000000..09b0526 --- /dev/null +++ b/core/res/res/drawable-xlarge/unlock_halo.png diff --git a/core/res/res/drawable-xlarge/unlock_ring.png b/core/res/res/drawable-xlarge/unlock_ring.png Binary files differnew file mode 100644 index 0000000..1ac6d54 --- /dev/null +++ b/core/res/res/drawable-xlarge/unlock_ring.png diff --git a/core/res/res/drawable-xlarge/unlock_wave.png b/core/res/res/drawable-xlarge/unlock_wave.png Binary files differnew file mode 100644 index 0000000..21bfa24 --- /dev/null +++ b/core/res/res/drawable-xlarge/unlock_wave.png diff --git a/core/res/res/layout-xlarge/keyguard_screen_tab_unlock.xml b/core/res/res/layout-xlarge/keyguard_screen_tab_unlock.xml index 4761800..b3645aa 100644 --- a/core/res/res/layout-xlarge/keyguard_screen_tab_unlock.xml +++ b/core/res/res/layout-xlarge/keyguard_screen_tab_unlock.xml @@ -57,13 +57,10 @@ android:drawablePadding="4dip" /> - <com.android.internal.widget.SlidingTab - android:id="@+id/tab_selector" - android:orientation="horizontal" - android:layout_width="match_parent" + <com.android.internal.widget.WaveView + android:id="@+id/wave_view" + android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_alignParentBottom="true" - android:layout_marginBottom="80dip" /> <!-- "emergency calls only" shown when sim is missing or PUKd --> diff --git a/core/res/res/layout-xlarge/keyguard_screen_tab_unlock_land.xml b/core/res/res/layout-xlarge/keyguard_screen_tab_unlock_land.xml index bb398f6..6c99cca 100644 --- a/core/res/res/layout-xlarge/keyguard_screen_tab_unlock_land.xml +++ b/core/res/res/layout-xlarge/keyguard_screen_tab_unlock_land.xml @@ -58,15 +58,14 @@ android:drawablePadding="4dip" /> - <com.android.internal.widget.SlidingTab - android:id="@+id/tab_selector" - android:orientation="vertical" + <com.android.internal.widget.WaveView + android:id="@+id/wave_view" android:layout_width="wrap_content" android:layout_height="match_parent" android:layout_marginRight="0dip" android:layout_weight="1.0" /> - + <!-- "emergency calls only" shown when sim is missing or PUKd --> <TextView android:id="@+id/emergencyCallText" diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml index 94cc393..e6c0a48 100644 --- a/core/res/res/values-cs/strings.xml +++ b/core/res/res/values-cs/strings.xml @@ -266,8 +266,10 @@ <string name="permdesc_clearAppCache" msgid="7740465694193671402">"Umožňuje aplikaci uvolnit paměť telefonu smazáním souborů v adresáři mezipaměti aplikace. Přístup je velmi omezený, většinou pouze pro systémové procesy."</string> <string name="permlab_movePackage" msgid="728454979946503926">"Přesun zdrojů aplikace"</string> <string name="permdesc_movePackage" msgid="6323049291923925277">"Umožňuje aplikaci přesunout své zdroje z interní paměti na externí médium a opačně."</string> - <string name="permlab_readLogs" msgid="4811921703882532070">"čtení systémových souborů protokolu"</string> - <string name="permdesc_readLogs" msgid="2257937955580475902">"Umožňuje aplikaci číst různé systémové soubory protokolů. Toto nastavení aplikaci umožní získat obecné informace o činnostech s telefonem, ale neměly by obsahovat žádné osobní či soukromé informace."</string> + <!-- no translation found for permlab_readLogs (6615778543198967614) --> + <skip /> + <!-- no translation found for permdesc_readLogs (8896449437464867766) --> + <skip /> <string name="permlab_diagnostic" msgid="8076743953908000342">"čtení nebo zápis do prostředků funkce diag"</string> <string name="permdesc_diagnostic" msgid="3121238373951637049">"Umožňuje aplikaci číst libovolné prostředky ve skupině diag, např. soubory ve složce /dev, a zapisovat do nich. Může dojít k ovlivnění stability a bezpečnosti systému. Toto nastavení by měl používat pouze výrobce či operátor pro diagnostiku hardwaru."</string> <string name="permlab_changeComponentState" msgid="79425198834329406">"povolení či zakázání komponent aplikací"</string> @@ -434,8 +436,7 @@ <string name="policydesc_wipeData" msgid="2314060933796396205">"Bez dalšího potvrzení obnoví výchozí nastavení z výroby a smaže všechna data."</string> <!-- no translation found for policylab_setGlobalProxy (2784828293747791446) --> <skip /> - <!-- no translation found for policydesc_setGlobalProxy (6387497466660154931) --> - <skip /> + <string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"Nastavte globální server proxy, který je používán, když jsou zásady aktivní. Platný globální server proxy nastavuje pouze první správce zařízení."</string> <string-array name="phoneTypes"> <item msgid="8901098336658710359">"Domů"</item> <item msgid="869923650527136615">"Mobil"</item> @@ -529,6 +530,14 @@ <string name="orgTypeWork" msgid="29268870505363872">"Práce"</string> <string name="orgTypeOther" msgid="3951781131570124082">"Jiné"</string> <string name="orgTypeCustom" msgid="225523415372088322">"Vlastní"</string> + <!-- no translation found for sipAddressTypeCustom (2473580593111590945) --> + <skip /> + <!-- no translation found for sipAddressTypeHome (6093598181069359295) --> + <skip /> + <!-- no translation found for sipAddressTypeWork (6920725730797099047) --> + <skip /> + <!-- no translation found for sipAddressTypeOther (4408436162950119849) --> + <skip /> <string name="keyguard_password_enter_pin_code" msgid="3731488827218876115">"Zadejte kód PIN"</string> <string name="keyguard_password_enter_password_code" msgid="9138158344813213754">"Zadejte heslo pro odblokování"</string> <string name="keyguard_password_enter_pin_password_code" msgid="638347075625491514">"Zadejte kód PIN pro odblokování"</string> @@ -589,6 +598,8 @@ <string name="js_dialog_before_unload" msgid="1901675448179653089">"Chcete opustit tuto stránku?"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n"Vyberte OK, chcete-li pokračovat, nebo Zrušit, chcete-li na stránce zůstat."</string> <string name="save_password_label" msgid="6860261758665825069">"Potvrdit"</string> <string name="double_tap_toast" msgid="1068216937244567247">"Tip: Dvojitým klepnutím můžete zobrazení přiblížit nebo oddálit."</string> + <!-- no translation found for autofill_this_form (8940110866775097494) --> + <skip /> <string name="permlab_readHistoryBookmarks" msgid="1284843728203412135">"čtení historie a záložek Prohlížeče"</string> <string name="permdesc_readHistoryBookmarks" msgid="4981489815467617191">"Umožňuje aplikaci číst všechny navštívené adresy URL a záložky Prohlížeče."</string> <string name="permlab_writeHistoryBookmarks" msgid="9009434109836280374">"zápis do historie a záložek Prohlížeče"</string> @@ -875,10 +886,10 @@ <string name="throttle_warning_notification_message" msgid="2609734763845705708">"Dotykem zobrazíte další informace o využití mobilních dat"</string> <string name="throttled_notification_title" msgid="6269541897729781332">"Byl překročen limit mobilních dat"</string> <string name="throttled_notification_message" msgid="4712369856601275146">"Dotykem zobrazíte další informace o využití mobilních dat"</string> - <!-- no translation found for no_matches (8129421908915840737) --> - <skip /> - <!-- no translation found for find_on_page (1946799233822820384) --> - <skip /> - <!-- no translation found for matches_found:one (8167147081136579439) --> - <!-- no translation found for matches_found:other (4641872797067609177) --> + <string name="no_matches" msgid="8129421908915840737">"Žádné shody"</string> + <string name="find_on_page" msgid="1946799233822820384">"Vyhledat na stránce"</string> + <plurals name="matches_found"> + <item quantity="one" msgid="8167147081136579439">"1 shoda"</item> + <item quantity="other" msgid="4641872797067609177">"<xliff:g id="INDEX">%d</xliff:g> z <xliff:g id="TOTAL">%d</xliff:g>"</item> + </plurals> </resources> diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml index d64b548..d7e3625 100644 --- a/core/res/res/values-da/strings.xml +++ b/core/res/res/values-da/strings.xml @@ -266,8 +266,10 @@ <string name="permdesc_clearAppCache" msgid="7740465694193671402">"Tillader, at et program frigør plads på telefonen ved at slette filer i programmets cachemappe. Adgang er normalt meget begrænset til systemprocesser."</string> <string name="permlab_movePackage" msgid="728454979946503926">"Flyt programressourcer"</string> <string name="permdesc_movePackage" msgid="6323049291923925277">"Tillader, at et program flytter programressourcer fra interne til eksterne medier og omvendt."</string> - <string name="permlab_readLogs" msgid="4811921703882532070">"læs systemlogfiler"</string> - <string name="permdesc_readLogs" msgid="2257937955580475902">"Tillader, at et program læser fra systemets forskellige logfiler. Dermed kan generelle oplysninger om, hvad du laver med telefonen, registreres, men logfilerne bør ikke indeholde personlige eller private oplysninger."</string> + <!-- no translation found for permlab_readLogs (6615778543198967614) --> + <skip /> + <!-- no translation found for permdesc_readLogs (8896449437464867766) --> + <skip /> <string name="permlab_diagnostic" msgid="8076743953908000342">"læs/skriv til ressourcer ejet af diag"</string> <string name="permdesc_diagnostic" msgid="3121238373951637049">"Tillader, at et program læser og skriver til alle ressourcer, der ejes af diag-gruppen, som f.eks. flier i /dev. Dette kan muligvis påvirke systemets stabilitet og sikkerhed. Dette bør KUN bruges til hardwarespecifikke diagnosticeringer foretaget af producent eller udbyder."</string> <string name="permlab_changeComponentState" msgid="79425198834329406">"aktiver eller deaktiver programkomponenter"</string> @@ -434,8 +436,7 @@ <string name="policydesc_wipeData" msgid="2314060933796396205">"Foretag en fabriksnulstilling, der sletter alle dine data uden bekræftelse."</string> <!-- no translation found for policylab_setGlobalProxy (2784828293747791446) --> <skip /> - <!-- no translation found for policydesc_setGlobalProxy (6387497466660154931) --> - <skip /> + <string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"Angiv enhedens globale proxy, der skal bruges, mens politikken er aktiveret. Kun den første enhedsadministrator angiver den effektive globale proxy."</string> <string-array name="phoneTypes"> <item msgid="8901098336658710359">"Hjem"</item> <item msgid="869923650527136615">"Mobil"</item> @@ -529,6 +530,14 @@ <string name="orgTypeWork" msgid="29268870505363872">"Arbejde"</string> <string name="orgTypeOther" msgid="3951781131570124082">"Andre"</string> <string name="orgTypeCustom" msgid="225523415372088322">"Tilpasset"</string> + <!-- no translation found for sipAddressTypeCustom (2473580593111590945) --> + <skip /> + <!-- no translation found for sipAddressTypeHome (6093598181069359295) --> + <skip /> + <!-- no translation found for sipAddressTypeWork (6920725730797099047) --> + <skip /> + <!-- no translation found for sipAddressTypeOther (4408436162950119849) --> + <skip /> <string name="keyguard_password_enter_pin_code" msgid="3731488827218876115">"Indtast PIN-kode"</string> <string name="keyguard_password_enter_password_code" msgid="9138158344813213754">"Indtast adgangskode for at låse op"</string> <string name="keyguard_password_enter_pin_password_code" msgid="638347075625491514">"Indtast pinkode for at låse op"</string> @@ -589,6 +598,8 @@ <string name="js_dialog_before_unload" msgid="1901675448179653089">"Naviger væk fra denne side?"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n" Vælg OK for at fortsætte eller Annuller for at blive på den aktuelle side."</string> <string name="save_password_label" msgid="6860261758665825069">"Bekræft"</string> <string name="double_tap_toast" msgid="1068216937244567247">"Tip: Dobbeltklik for at zoome ind eller ud."</string> + <!-- no translation found for autofill_this_form (8940110866775097494) --> + <skip /> <string name="permlab_readHistoryBookmarks" msgid="1284843728203412135">"læs browserens oversigt og bogmærker"</string> <string name="permdesc_readHistoryBookmarks" msgid="4981489815467617191">"Tillader, at programmet læser alle de webadresser, browseren har besøgt, og alle browserens bogmærker."</string> <string name="permlab_writeHistoryBookmarks" msgid="9009434109836280374">"skriv browserens oversigt og bogmærker"</string> @@ -875,10 +886,10 @@ <string name="throttle_warning_notification_message" msgid="2609734763845705708">"Tryk for oplysninger om brug af mobildata"</string> <string name="throttled_notification_title" msgid="6269541897729781332">"Grænsen for mobildata er overskredet"</string> <string name="throttled_notification_message" msgid="4712369856601275146">"Tryk for oplysninger om brug af mobildata"</string> - <!-- no translation found for no_matches (8129421908915840737) --> - <skip /> - <!-- no translation found for find_on_page (1946799233822820384) --> - <skip /> - <!-- no translation found for matches_found:one (8167147081136579439) --> - <!-- no translation found for matches_found:other (4641872797067609177) --> + <string name="no_matches" msgid="8129421908915840737">"Der er ingen matches"</string> + <string name="find_on_page" msgid="1946799233822820384">"Find på siden"</string> + <plurals name="matches_found"> + <item quantity="one" msgid="8167147081136579439">"1 match"</item> + <item quantity="other" msgid="4641872797067609177">"<xliff:g id="INDEX">%d</xliff:g> af <xliff:g id="TOTAL">%d</xliff:g>"</item> + </plurals> </resources> diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml index 1fe084c..bbe8de9 100644 --- a/core/res/res/values-de/strings.xml +++ b/core/res/res/values-de/strings.xml @@ -266,8 +266,10 @@ <string name="permdesc_clearAppCache" msgid="7740465694193671402">"Ermöglicht einer Anwendung, Telefonspeicher durch das Löschen von Dateien im Cache-Verzeichnis der Anwendung freizugeben. Der Zugriff beschränkt sich in der Regel auf Systemprozesse."</string> <string name="permlab_movePackage" msgid="728454979946503926">"Anwendungsressourcen verschieben"</string> <string name="permdesc_movePackage" msgid="6323049291923925277">"Ermöglicht einer Anwendung, Anwendungsressourcen von interne auf externe Medien zu verschieben und umgekehrt."</string> - <string name="permlab_readLogs" msgid="4811921703882532070">"System-Protokolldateien lesen"</string> - <string name="permdesc_readLogs" msgid="2257937955580475902">"Ermöglicht einer Anwendung, die verschiedenen Protokolldateien des Systems zu lesen. So können allgemeine Informationen zu den auf Ihrem Telefon durchgeführten Aktionen eingesehen werden, diese sollten jedoch keine persönlichen oder geheimen Daten enthalten."</string> + <!-- no translation found for permlab_readLogs (6615778543198967614) --> + <skip /> + <!-- no translation found for permdesc_readLogs (8896449437464867766) --> + <skip /> <string name="permlab_diagnostic" msgid="8076743953908000342">"Lese-/Schreibberechtigung für zu Diagnosegruppe gehörige Elemente"</string> <string name="permdesc_diagnostic" msgid="3121238373951637049">"Ermöglicht einer Anwendung, alle Elemente in der Diagnosegruppe zu lesen und zu bearbeiten, etwa Dateien in \"/dev\". Dies könnte eine potenzielle Gefährdung für die Stabilität und Sicherheit des Systems darstellen und sollte NUR für Hardware-spezifische Diagnosen des Herstellers oder Netzbetreibers verwendet werden."</string> <string name="permlab_changeComponentState" msgid="79425198834329406">"Anwendungskomponenten aktivieren oder deaktivieren"</string> @@ -434,8 +436,7 @@ <string name="policydesc_wipeData" msgid="2314060933796396205">"Zurücksetzen auf die Werkseinstellungen. Dabei werden alle Ihre Daten ohne Nachfrage gelöscht."</string> <!-- no translation found for policylab_setGlobalProxy (2784828293747791446) --> <skip /> - <!-- no translation found for policydesc_setGlobalProxy (6387497466660154931) --> - <skip /> + <string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"Den globalen Proxy des Geräts zur Verwendung während der Aktivierung der Richtlinie festlegen. Nur der erste Geräteadministrator kann den gültigen globalen Proxy festlegen."</string> <string-array name="phoneTypes"> <item msgid="8901098336658710359">"Privat"</item> <item msgid="869923650527136615">"Mobil"</item> @@ -529,6 +530,14 @@ <string name="orgTypeWork" msgid="29268870505363872">"Beruflich"</string> <string name="orgTypeOther" msgid="3951781131570124082">"Andere"</string> <string name="orgTypeCustom" msgid="225523415372088322">"Benutzerdefiniert"</string> + <!-- no translation found for sipAddressTypeCustom (2473580593111590945) --> + <skip /> + <!-- no translation found for sipAddressTypeHome (6093598181069359295) --> + <skip /> + <!-- no translation found for sipAddressTypeWork (6920725730797099047) --> + <skip /> + <!-- no translation found for sipAddressTypeOther (4408436162950119849) --> + <skip /> <string name="keyguard_password_enter_pin_code" msgid="3731488827218876115">"PIN-Code eingeben"</string> <string name="keyguard_password_enter_password_code" msgid="9138158344813213754">"Passwort zum Entsperren eingeben"</string> <string name="keyguard_password_enter_pin_password_code" msgid="638347075625491514">"PIN zum Entsperren eingeben"</string> @@ -539,7 +548,7 @@ <string name="lockscreen_screen_locked" msgid="7288443074806832904">"Display gesperrt."</string> <string name="lockscreen_instructions_when_pattern_enabled" msgid="46154051614126049">"Drücken Sie die Menütaste, um das Telefon zu entsperren oder einen Notruf zu tätigen."</string> <string name="lockscreen_instructions_when_pattern_disabled" msgid="686260028797158364">"Zum Entsperren die Menütaste drücken"</string> - <string name="lockscreen_pattern_instructions" msgid="7478703254964810302">"Schema für Entsperrung zeichnen"</string> + <string name="lockscreen_pattern_instructions" msgid="7478703254964810302">"Muster zum Entsperren zeichnen"</string> <string name="lockscreen_emergency_call" msgid="5347633784401285225">"Notruf"</string> <string name="lockscreen_return_to_call" msgid="5244259785500040021">"Zurück zum Anruf"</string> <string name="lockscreen_pattern_correct" msgid="9039008650362261237">"Korrekt!"</string> @@ -589,6 +598,8 @@ <string name="js_dialog_before_unload" msgid="1901675448179653089">"Von dieser Seite navigieren?"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n"Wählen Sie \"OK\", um fortzufahren, oder wählen Sie \"Abbrechen\", um auf der aktuellen Seite zu bleiben."</string> <string name="save_password_label" msgid="6860261758665825069">"Bestätigen"</string> <string name="double_tap_toast" msgid="1068216937244567247">"Tipp: Zum Heranzoomen und Vergrößern zweimal tippen"</string> + <!-- no translation found for autofill_this_form (8940110866775097494) --> + <skip /> <string name="permlab_readHistoryBookmarks" msgid="1284843728203412135">"Browserverlauf und Lesezeichen lesen"</string> <string name="permdesc_readHistoryBookmarks" msgid="4981489815467617191">"Ermöglicht der Anwendung, alle URLs, die mit dem Browser besucht wurden, sowie alle Lesezeichen des Browsers zu lesen."</string> <string name="permlab_writeHistoryBookmarks" msgid="9009434109836280374">"Browserverlauf und Lesezeichen schreiben"</string> @@ -795,7 +806,7 @@ <string name="usb_storage_button_mount" msgid="1052259930369508235">"USB-Speicher aktivieren"</string> <string name="usb_storage_error_message" msgid="2534784751603345363">"Bei der Verwendung Ihrer SD-Karte als USB-Speicher ist ein Problem aufgetreten."</string> <string name="usb_storage_notification_title" msgid="8175892554757216525">"USB-Verbindung"</string> - <string name="usb_storage_notification_message" msgid="7380082404288219341">"Wählen Sie die Dateien aus, die von Ihrem oder auf Ihren Computer kopiert werden sollen."</string> + <string name="usb_storage_notification_message" msgid="7380082404288219341">"Zum Kopieren von Dateien zu/von Ihrem Computer."</string> <string name="usb_storage_stop_notification_title" msgid="2336058396663516017">"USB-Speicher deaktivieren"</string> <string name="usb_storage_stop_notification_message" msgid="2591813490269841539">"Auswählen, um USB-Speicher zu deaktivieren."</string> <string name="usb_storage_stop_title" msgid="660129851708775853">"USB-Speicher in Verwendung"</string> @@ -816,7 +827,7 @@ <string name="fast_scroll_numeric_alphabet" msgid="4030170524595123610">" 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"</string> <string name="candidates_style" msgid="4333913089637062257"><u>"Kandidaten"</u></string> <string name="ext_media_checking_notification_title" msgid="5457603418970994050">"SD-Karte wird vorbereitet"</string> - <string name="ext_media_checking_notification_message" msgid="8287319882926737053">"Nach Fehlern wird gesucht."</string> + <string name="ext_media_checking_notification_message" msgid="8287319882926737053">"Suche nach Fehlern"</string> <string name="ext_media_nofs_notification_title" msgid="780477838241212997">"SD-Karte leer"</string> <string name="ext_media_nofs_notification_message" msgid="3817704088027829380">"SD-Karte ist leer oder verfügt über ein nicht unterstütztes Dateisystem."</string> <string name="ext_media_unmountable_notification_title" msgid="6410723906019100189">"Beschädigte SD-Karte"</string> @@ -875,10 +886,10 @@ <string name="throttle_warning_notification_message" msgid="2609734763845705708">"Weitere Informationen über die Mobildatennutzung durch Berühren aufrufen"</string> <string name="throttled_notification_title" msgid="6269541897729781332">"Mobildatenlimit überschritten"</string> <string name="throttled_notification_message" msgid="4712369856601275146">"Weitere Informationen über die Mobildatennutzung durch Berühren aufrufen"</string> - <!-- no translation found for no_matches (8129421908915840737) --> - <skip /> - <!-- no translation found for find_on_page (1946799233822820384) --> - <skip /> - <!-- no translation found for matches_found:one (8167147081136579439) --> - <!-- no translation found for matches_found:other (4641872797067609177) --> + <string name="no_matches" msgid="8129421908915840737">"Keine Übereinstimmungen"</string> + <string name="find_on_page" msgid="1946799233822820384">"Auf Seite suchen"</string> + <plurals name="matches_found"> + <item quantity="one" msgid="8167147081136579439">"1 Übereinstimmung"</item> + <item quantity="other" msgid="4641872797067609177">"<xliff:g id="INDEX">%d</xliff:g> von <xliff:g id="TOTAL">%d</xliff:g>"</item> + </plurals> </resources> diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml index 2d54f44..e2a01f4 100644 --- a/core/res/res/values-el/strings.xml +++ b/core/res/res/values-el/strings.xml @@ -266,8 +266,10 @@ <string name="permdesc_clearAppCache" msgid="7740465694193671402">"Επιτρέπει σε μια εφαρμογή να αυξήσει τον ελεύθερο χώρο αποθήκευσης του τηλεφώνου διαγράφοντας αρχεία από τον κατάλογο προσωρινής μνήμης της εφαρμογής. Η πρόσβαση είναι συνήθως πολύ περιορισμένη στη διαδικασία συστήματος."</string> <string name="permlab_movePackage" msgid="728454979946503926">"Μετακίνηση πόρων εφαρμογής"</string> <string name="permdesc_movePackage" msgid="6323049291923925277">"Επιτρέπει σε μια εφαρμογή τη μετακίνηση πόρων εφαρμογής από ένα εσωτερικό σε ένα εξωτερικό μέσο και αντίστροφα."</string> - <string name="permlab_readLogs" msgid="4811921703882532070">"ανάγνωση αρχείων καταγραφής συστήματος"</string> - <string name="permdesc_readLogs" msgid="2257937955580475902">"Επιτρέπει σε μια εφαρμογή να αναγνώσει τα αρχεία καταγραφής του συστήματος. Έτσι μπορεί να ανακαλύψει γενικές πληροφορίες σχετικά με τις δραστηριότητές σας στο τηλέφωνο, όμως δεν θα πρέπει να περιέχουν προσωπικές ή ιδιωτικές πληροφορίες."</string> + <!-- no translation found for permlab_readLogs (6615778543198967614) --> + <skip /> + <!-- no translation found for permdesc_readLogs (8896449437464867766) --> + <skip /> <string name="permlab_diagnostic" msgid="8076743953908000342">"ανάγνωση/εγγραφή σε πόρους που ανήκουν στο διαγνωστικό"</string> <string name="permdesc_diagnostic" msgid="3121238373951637049">"Επιτρέπει σε μια εφαρμογή την ανάγνωση και την εγγραφή σε πόρο που ανήκει στην ομάδα διαγνωστικού (π.χ. αρχεία στον κατάλογο /dev). Αυτό ενδέχεται να επηρεάσει την σταθερότητα και την ασφάλεια του συστήματος. Θα πρέπει να χρησιμοποιείται ΜΟΝΟ για διαγνωστικά υλικού του κατασκευαστή ή του χειριστή."</string> <string name="permlab_changeComponentState" msgid="79425198834329406">"ενεργοποίηση ή απενεργοποίηση στοιχείων εφαρμογής"</string> @@ -434,8 +436,7 @@ <string name="policydesc_wipeData" msgid="2314060933796396205">"Πραγματοποιείται επαναφορά εργοστασιακών ρυθμίσεων, με τη διαγραφή όλων των δεδομένων σας χωρίς επιβεβαίωση."</string> <!-- no translation found for policylab_setGlobalProxy (2784828293747791446) --> <skip /> - <!-- no translation found for policydesc_setGlobalProxy (6387497466660154931) --> - <skip /> + <string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"Ορίστε τη χρήση του γενικού διακομιστή μεσολάβησης της συσκευής όταν είναι ενεργοποιημένη η πολιτική. Μόνο ο διαχειριστής της πρώτης συσκευής ορίζει τον ισχύοντα γενικό διακομιστή μεσολάβησης."</string> <string-array name="phoneTypes"> <item msgid="8901098336658710359">"Οικία"</item> <item msgid="869923650527136615">"Κινητό"</item> @@ -529,6 +530,14 @@ <string name="orgTypeWork" msgid="29268870505363872">"Εργασία"</string> <string name="orgTypeOther" msgid="3951781131570124082">"Άλλο"</string> <string name="orgTypeCustom" msgid="225523415372088322">"Προσαρμοσμένο"</string> + <!-- no translation found for sipAddressTypeCustom (2473580593111590945) --> + <skip /> + <!-- no translation found for sipAddressTypeHome (6093598181069359295) --> + <skip /> + <!-- no translation found for sipAddressTypeWork (6920725730797099047) --> + <skip /> + <!-- no translation found for sipAddressTypeOther (4408436162950119849) --> + <skip /> <string name="keyguard_password_enter_pin_code" msgid="3731488827218876115">"Πληκτρολογήστε τον κωδικό αριθμό PIN"</string> <string name="keyguard_password_enter_password_code" msgid="9138158344813213754">"Εισαγάγετε τον κωδικό πρόσβασης για ξεκλείδωμα"</string> <string name="keyguard_password_enter_pin_password_code" msgid="638347075625491514">"Εισαγάγετε το PIN για ξεκλείδωμα"</string> @@ -589,6 +598,8 @@ <string name="js_dialog_before_unload" msgid="1901675448179653089">"Απομάκρυνση από αυτή τη σελίδα;"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n"Επιλέξτε OK για συνέχεια, ή Ακύρωση για παραμονή στην τρέχουσα σελίδα."</string> <string name="save_password_label" msgid="6860261758665825069">"Επιβεβαίωση"</string> <string name="double_tap_toast" msgid="1068216937244567247">"Συμβουλή: διπλό άγγιγμα για μεγέθυνση και σμίκρυνση."</string> + <!-- no translation found for autofill_this_form (8940110866775097494) --> + <skip /> <string name="permlab_readHistoryBookmarks" msgid="1284843728203412135">"ανάγνωση ιστορικού και σελιδοδεικτών προγράμματος περιήγησης"</string> <string name="permdesc_readHistoryBookmarks" msgid="4981489815467617191">"Επιτρέπει στην εφαρμογή την ανάγνωση όλων των διευθύνσεων URL που το πρόγραμμα περιήγησης έχει επισκεφθεί και όλων των σελιδοδεικτών του προγράμματος περιήγησης."</string> <string name="permlab_writeHistoryBookmarks" msgid="9009434109836280374">"εγγραφή ιστορικού και σελιδοδεικτών προγράμματος περιήγησης"</string> @@ -875,10 +886,10 @@ <string name="throttle_warning_notification_message" msgid="2609734763845705708">"Αγγίξτε για να μάθετε περισσότερα σχετικά με τη χρήση δεδομένων κινητής τηλεφωνίας"</string> <string name="throttled_notification_title" msgid="6269541897729781332">"Ξεπεράστηκε το όριο δεδομένων κινητής τηλεφωνίας"</string> <string name="throttled_notification_message" msgid="4712369856601275146">"Αγγίξτε για να μάθετε περισσότερα σχετικά με τη χρήση δεδομένων κινητής τηλεφωνίας"</string> - <!-- no translation found for no_matches (8129421908915840737) --> - <skip /> - <!-- no translation found for find_on_page (1946799233822820384) --> - <skip /> - <!-- no translation found for matches_found:one (8167147081136579439) --> - <!-- no translation found for matches_found:other (4641872797067609177) --> + <string name="no_matches" msgid="8129421908915840737">"Δεν υπάρχουν αποτελέσματα"</string> + <string name="find_on_page" msgid="1946799233822820384">"Εύρεση στη σελίδα"</string> + <plurals name="matches_found"> + <item quantity="one" msgid="8167147081136579439">"1 αποτέλεσμα"</item> + <item quantity="other" msgid="4641872797067609177">"<xliff:g id="INDEX">%d</xliff:g> από <xliff:g id="TOTAL">%d</xliff:g>"</item> + </plurals> </resources> diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml index efd2885..fe27b95 100644 --- a/core/res/res/values-es-rUS/strings.xml +++ b/core/res/res/values-es-rUS/strings.xml @@ -266,8 +266,10 @@ <string name="permdesc_clearAppCache" msgid="7740465694193671402">"Admite una aplicación que libera espacio de almacenamiento en el teléfono al eliminar archivos del directorio de memoria caché de la aplicación. En general, el acceso es muy restringido para el proceso del sistema."</string> <string name="permlab_movePackage" msgid="728454979946503926">"Mover recursos de la aplicación"</string> <string name="permdesc_movePackage" msgid="6323049291923925277">"Permite a una aplicación mover recursos de aplicación de medios internos a externos y viceversa."</string> - <string name="permlab_readLogs" msgid="4811921703882532070">"leer archivos de registro del sistema"</string> - <string name="permdesc_readLogs" msgid="2257937955580475902">"Admite una aplicación que lee diversos archivos de registro del sistema. Esto le permite descubrir información general sobre lo que haces con el teléfono, pero no debe contener información personal ni privada."</string> + <!-- no translation found for permlab_readLogs (6615778543198967614) --> + <skip /> + <!-- no translation found for permdesc_readLogs (8896449437464867766) --> + <skip /> <string name="permlab_diagnostic" msgid="8076743953908000342">"leer y escribir a recursos dentro del grupo de diagnóstico"</string> <string name="permdesc_diagnostic" msgid="3121238373951637049">"Admite una aplicación que lee y escribe a cualquier recurso dentro del grupo de diagnóstico; por ejemplo, archivos con /dev. Esto puede afectar potencialmente la estabilidad y la seguridad del sistema. Debe utilizarlo SÓLO el fabricante o el operador en los diagnósticos específicos del hardware."</string> <string name="permlab_changeComponentState" msgid="79425198834329406">"activar o desactivar componentes de la aplicación"</string> @@ -434,8 +436,7 @@ <string name="policydesc_wipeData" msgid="2314060933796396205">"Realizar un reestablecimiento de fábrica y borrar todos tus datos sin ninguna confirmación."</string> <!-- no translation found for policylab_setGlobalProxy (2784828293747791446) --> <skip /> - <!-- no translation found for policydesc_setGlobalProxy (6387497466660154931) --> - <skip /> + <string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"Configuración del proxy global de dispositivo que se utilizará mientras se habilita la política. Sólo la primera administración de dispositivo configura el proxy global efectivo."</string> <string-array name="phoneTypes"> <item msgid="8901098336658710359">"Casa"</item> <item msgid="869923650527136615">"Celular"</item> @@ -529,6 +530,14 @@ <string name="orgTypeWork" msgid="29268870505363872">"Trabajo"</string> <string name="orgTypeOther" msgid="3951781131570124082">"Otro"</string> <string name="orgTypeCustom" msgid="225523415372088322">"Personalizado"</string> + <!-- no translation found for sipAddressTypeCustom (2473580593111590945) --> + <skip /> + <!-- no translation found for sipAddressTypeHome (6093598181069359295) --> + <skip /> + <!-- no translation found for sipAddressTypeWork (6920725730797099047) --> + <skip /> + <!-- no translation found for sipAddressTypeOther (4408436162950119849) --> + <skip /> <string name="keyguard_password_enter_pin_code" msgid="3731488827218876115">"Ingresar el código de PIN"</string> <string name="keyguard_password_enter_password_code" msgid="9138158344813213754">"Ingresar la contraseña para desbloquear"</string> <string name="keyguard_password_enter_pin_password_code" msgid="638347075625491514">"Ingresa el PIN para desbloquear"</string> @@ -589,6 +598,8 @@ <string name="js_dialog_before_unload" msgid="1901675448179653089">"¿Deseas salir de esta página?"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n"Selecciona Aceptar para continuar o Cancelar para permanecer en la página actual."</string> <string name="save_password_label" msgid="6860261758665825069">"Confirmar"</string> <string name="double_tap_toast" msgid="1068216937244567247">"Sugerencia: presiona dos veces para acercar y alejar"</string> + <!-- no translation found for autofill_this_form (8940110866775097494) --> + <skip /> <string name="permlab_readHistoryBookmarks" msgid="1284843728203412135">"leer historial y marcadores del navegador"</string> <string name="permdesc_readHistoryBookmarks" msgid="4981489815467617191">"Permite a la aplicación leer todas las URL que ha visitado el navegador y todos los marcadores del navegador."</string> <string name="permlab_writeHistoryBookmarks" msgid="9009434109836280374">"escribir historial y marcadores del navegador"</string> @@ -875,10 +886,10 @@ <string name="throttle_warning_notification_message" msgid="2609734763845705708">"Toca para obtener más información acerca de la utilización de datos móviles."</string> <string name="throttled_notification_title" msgid="6269541897729781332">"Límite de datos móviles excedido "</string> <string name="throttled_notification_message" msgid="4712369856601275146">"Toca para obtener más información acerca de la utilización de datos móviles."</string> - <!-- no translation found for no_matches (8129421908915840737) --> - <skip /> - <!-- no translation found for find_on_page (1946799233822820384) --> - <skip /> - <!-- no translation found for matches_found:one (8167147081136579439) --> - <!-- no translation found for matches_found:other (4641872797067609177) --> + <string name="no_matches" msgid="8129421908915840737">"Sin coincidencias"</string> + <string name="find_on_page" msgid="1946799233822820384">"Buscar en la página"</string> + <plurals name="matches_found"> + <item quantity="one" msgid="8167147081136579439">"1 coincidencia"</item> + <item quantity="other" msgid="4641872797067609177">"<xliff:g id="INDEX">%d</xliff:g> de <xliff:g id="TOTAL">%d</xliff:g>"</item> + </plurals> </resources> diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml index b12f8be..10ecaca 100644 --- a/core/res/res/values-es/strings.xml +++ b/core/res/res/values-es/strings.xml @@ -266,8 +266,10 @@ <string name="permdesc_clearAppCache" msgid="7740465694193671402">"Permite que una aplicación libere espacio de almacenamiento en el teléfono mediante la eliminación de archivos en el directorio de caché de la aplicación. El acceso al proceso del sistema suele estar muy restringido."</string> <string name="permlab_movePackage" msgid="728454979946503926">"Mover recursos de aplicaciones"</string> <string name="permdesc_movePackage" msgid="6323049291923925277">"Permite que una aplicación mueva los recursos de aplicaciones de un medio interno a otro externo y viceversa."</string> - <string name="permlab_readLogs" msgid="4811921703882532070">"leer archivos de registro del sistema"</string> - <string name="permdesc_readLogs" msgid="2257937955580475902">"Permite que una aplicación lea los distintos archivos de registro del sistema. Con este permiso, la aplicación puede ver información general sobre las acciones que realiza el usuario con el teléfono, pero los registros no deberían contener información personal o privada."</string> + <!-- no translation found for permlab_readLogs (6615778543198967614) --> + <skip /> + <!-- no translation found for permdesc_readLogs (8896449437464867766) --> + <skip /> <string name="permlab_diagnostic" msgid="8076743953908000342">"leer/escribir en los recursos propiedad del grupo de diagnóstico"</string> <string name="permdesc_diagnostic" msgid="3121238373951637049">"Permite que una aplicación lea y escriba en cualquier recurso propiedad del grupo de diagnóstico como, por ejemplo, archivos in/dev. Este permiso podría afectar a la seguridad y estabilidad del sistema. SÓLO se debe utilizar para diagnósticos específicos de hardware realizados por el fabricante o el operador."</string> <string name="permlab_changeComponentState" msgid="79425198834329406">"habilitar o inhabilitar componentes de la aplicación"</string> @@ -434,8 +436,7 @@ <string name="policydesc_wipeData" msgid="2314060933796396205">"Permite realizar un restablecimiento de fábrica eliminando todos los datos sin confirmación."</string> <!-- no translation found for policylab_setGlobalProxy (2784828293747791446) --> <skip /> - <!-- no translation found for policydesc_setGlobalProxy (6387497466660154931) --> - <skip /> + <string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"Define el servidor proxy global que se debe utilizar mientras la política esté habilitada. Solo el primer administrador de dispositivos define el servidor proxy global efectivo."</string> <string-array name="phoneTypes"> <item msgid="8901098336658710359">"Casa"</item> <item msgid="869923650527136615">"Móvil"</item> @@ -529,6 +530,14 @@ <string name="orgTypeWork" msgid="29268870505363872">"Trabajo"</string> <string name="orgTypeOther" msgid="3951781131570124082">"Otra"</string> <string name="orgTypeCustom" msgid="225523415372088322">"Personalizada"</string> + <!-- no translation found for sipAddressTypeCustom (2473580593111590945) --> + <skip /> + <!-- no translation found for sipAddressTypeHome (6093598181069359295) --> + <skip /> + <!-- no translation found for sipAddressTypeWork (6920725730797099047) --> + <skip /> + <!-- no translation found for sipAddressTypeOther (4408436162950119849) --> + <skip /> <string name="keyguard_password_enter_pin_code" msgid="3731488827218876115">"Introduce el código PIN"</string> <string name="keyguard_password_enter_password_code" msgid="9138158344813213754">"Introducir contraseña para desbloquear"</string> <string name="keyguard_password_enter_pin_password_code" msgid="638347075625491514">"Introducir PIN para desbloquear"</string> @@ -589,6 +598,8 @@ <string name="js_dialog_before_unload" msgid="1901675448179653089">"¿Quieres salir de esta página?"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n"Selecciona \"Aceptar\" para continuar o \"Cancelar\" para permanecer en la página actual."</string> <string name="save_password_label" msgid="6860261758665825069">"Confirmar"</string> <string name="double_tap_toast" msgid="1068216937244567247">"Sugerencia: toca dos veces para ampliar o reducir."</string> + <!-- no translation found for autofill_this_form (8940110866775097494) --> + <skip /> <string name="permlab_readHistoryBookmarks" msgid="1284843728203412135">"leer información de marcadores y del historial del navegador"</string> <string name="permdesc_readHistoryBookmarks" msgid="4981489815467617191">"Permite que la aplicación lea todas las URL que ha visitado el navegador y todos sus marcadores."</string> <string name="permlab_writeHistoryBookmarks" msgid="9009434109836280374">"escribir en marcadores y en el historial del navegador"</string> @@ -875,10 +886,10 @@ <string name="throttle_warning_notification_message" msgid="2609734763845705708">"Más información sobre uso de datos"</string> <string name="throttled_notification_title" msgid="6269541897729781332">"Límite datos superado"</string> <string name="throttled_notification_message" msgid="4712369856601275146">"Más información sobre uso de datos"</string> - <!-- no translation found for no_matches (8129421908915840737) --> - <skip /> - <!-- no translation found for find_on_page (1946799233822820384) --> - <skip /> - <!-- no translation found for matches_found:one (8167147081136579439) --> - <!-- no translation found for matches_found:other (4641872797067609177) --> + <string name="no_matches" msgid="8129421908915840737">"No hay coincidencias."</string> + <string name="find_on_page" msgid="1946799233822820384">"Buscar en la página"</string> + <plurals name="matches_found"> + <item quantity="one" msgid="8167147081136579439">"Una coincidencia"</item> + <item quantity="other" msgid="4641872797067609177">"<xliff:g id="INDEX">%d</xliff:g> de <xliff:g id="TOTAL">%d</xliff:g>"</item> + </plurals> </resources> diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml index 5347a45..2492a85 100644 --- a/core/res/res/values-fr/strings.xml +++ b/core/res/res/values-fr/strings.xml @@ -266,8 +266,10 @@ <string name="permdesc_clearAppCache" msgid="7740465694193671402">"Permet à une application de libérer de l\'espace dans la mémoire du téléphone en supprimant des fichiers du répertoire du cache des applications. Cet accès est en général limité aux processus système."</string> <string name="permlab_movePackage" msgid="728454979946503926">"Déplacer des ressources d\'application"</string> <string name="permdesc_movePackage" msgid="6323049291923925277">"Autorise l\'application à déplacer des ressources d\'application d\'un support interne à un support externe et inversement."</string> - <string name="permlab_readLogs" msgid="4811921703882532070">"Lecture des fichiers journaux du système"</string> - <string name="permdesc_readLogs" msgid="2257937955580475902">"Permet à une application de lire les différents fichiers journaux du système afin d\'obtenir des informations générales sur la façon dont vous utilisez votre téléphone, sans pour autant récupérer des informations d\'ordre personnel ou privé."</string> + <!-- no translation found for permlab_readLogs (6615778543198967614) --> + <skip /> + <!-- no translation found for permdesc_readLogs (8896449437464867766) --> + <skip /> <string name="permlab_diagnostic" msgid="8076743953908000342">"Lecture/écriture dans les ressources appartenant aux diagnostics"</string> <string name="permdesc_diagnostic" msgid="3121238373951637049">"Permet à une application de lire et d\'éditer toute ressource appartenant au groupe de diagnostics (par exemple, les fichiers in/dev). Ceci peut affecter la stabilité et la sécurité du système. Cette fonctionnalité est UNIQUEMENT réservée aux diagnostics matériels effectués par le fabricant ou l\'opérateur."</string> <string name="permlab_changeComponentState" msgid="79425198834329406">"Activer ou désactiver des éléments de l\'application"</string> @@ -434,11 +436,10 @@ <string name="policydesc_wipeData" msgid="2314060933796396205">"Rétablit les paramètres d\'usine, supprimant toutes vos données sans demande de confirmation."</string> <!-- no translation found for policylab_setGlobalProxy (2784828293747791446) --> <skip /> - <!-- no translation found for policydesc_setGlobalProxy (6387497466660154931) --> - <skip /> + <string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"Indiquez le proxy global à utiliser pour ce mobile lorsque les règles sont activées. Seul l\'administrateur principal du mobile peut définir le proxy global effectif."</string> <string-array name="phoneTypes"> <item msgid="8901098336658710359">"Domicile"</item> - <item msgid="869923650527136615">"Portable"</item> + <item msgid="869923650527136615">"Mobile"</item> <item msgid="7897544654242874543">"Bureau"</item> <item msgid="1103601433382158155">"Télécopie bureau"</item> <item msgid="1735177144948329370">"Télécopie domicile"</item> @@ -529,6 +530,14 @@ <string name="orgTypeWork" msgid="29268870505363872">"Bureau"</string> <string name="orgTypeOther" msgid="3951781131570124082">"Autre"</string> <string name="orgTypeCustom" msgid="225523415372088322">"Personnalisé"</string> + <!-- no translation found for sipAddressTypeCustom (2473580593111590945) --> + <skip /> + <!-- no translation found for sipAddressTypeHome (6093598181069359295) --> + <skip /> + <!-- no translation found for sipAddressTypeWork (6920725730797099047) --> + <skip /> + <!-- no translation found for sipAddressTypeOther (4408436162950119849) --> + <skip /> <string name="keyguard_password_enter_pin_code" msgid="3731488827218876115">"Saisissez le code PIN"</string> <string name="keyguard_password_enter_password_code" msgid="9138158344813213754">"Saisissez le mot de passe pour procéder au déverrouillage."</string> <string name="keyguard_password_enter_pin_password_code" msgid="638347075625491514">"Saisissez le code PIN pour procéder au déverrouillage."</string> @@ -589,6 +598,8 @@ <string name="js_dialog_before_unload" msgid="1901675448179653089">"Vous souhaitez quitter cette page ?"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n"Sélectionnez OK pour continuer ou Annuler pour rester sur la page actuelle."</string> <string name="save_password_label" msgid="6860261758665825069">"Confirmer"</string> <string name="double_tap_toast" msgid="1068216937244567247">"Conseil : Appuyez deux fois pour effectuer un zoom avant ou arrière."</string> + <!-- no translation found for autofill_this_form (8940110866775097494) --> + <skip /> <string name="permlab_readHistoryBookmarks" msgid="1284843728203412135">"lire l\'historique et les favoris du navigateur"</string> <string name="permdesc_readHistoryBookmarks" msgid="4981489815467617191">"Autorise l\'application à lire toutes les URL auxquelles le navigateur a accédé et tous ses favoris."</string> <string name="permlab_writeHistoryBookmarks" msgid="9009434109836280374">"écrire dans l\'historique et les favoris du navigateur"</string> @@ -875,10 +886,10 @@ <string name="throttle_warning_notification_message" msgid="2609734763845705708">"Touchez pour en savoir plus sur l\'utilisation des données mobiles"</string> <string name="throttled_notification_title" msgid="6269541897729781332">"Quota d\'utilisation des données mobiles dépassé"</string> <string name="throttled_notification_message" msgid="4712369856601275146">"Touchez pour en savoir plus sur l\'utilisation des données mobiles"</string> - <!-- no translation found for no_matches (8129421908915840737) --> - <skip /> - <!-- no translation found for find_on_page (1946799233822820384) --> - <skip /> - <!-- no translation found for matches_found:one (8167147081136579439) --> - <!-- no translation found for matches_found:other (4641872797067609177) --> + <string name="no_matches" msgid="8129421908915840737">"Aucune correspondance"</string> + <string name="find_on_page" msgid="1946799233822820384">"Rechercher sur la page"</string> + <plurals name="matches_found"> + <item quantity="one" msgid="8167147081136579439">"1 correspondance"</item> + <item quantity="other" msgid="4641872797067609177">"<xliff:g id="INDEX">%d</xliff:g> sur <xliff:g id="TOTAL">%d</xliff:g>"</item> + </plurals> </resources> diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml index 713c983..ab37893 100644 --- a/core/res/res/values-it/strings.xml +++ b/core/res/res/values-it/strings.xml @@ -266,8 +266,10 @@ <string name="permdesc_clearAppCache" msgid="7740465694193671402">"Consente a un\'applicazione di liberare spazio sul telefono eliminando file nella directory della cache dell\'applicazione. L\'accesso è generalmente limitato a processi di sistema."</string> <string name="permlab_movePackage" msgid="728454979946503926">"Spostare risorse dell\'applicazione"</string> <string name="permdesc_movePackage" msgid="6323049291923925277">"Consente a un\'applicazione di spostare risorse applicative da supporti interni a esterni e viceversa."</string> - <string name="permlab_readLogs" msgid="4811921703882532070">"lettura file di registro sistema"</string> - <string name="permdesc_readLogs" msgid="2257937955580475902">"Consente a un\'applicazione di leggere vari file di registro del sistema per trovare informazioni generali sulle operazioni effettuate con il telefono. Tali file non dovrebbero contenere informazioni personali o riservate."</string> + <!-- no translation found for permlab_readLogs (6615778543198967614) --> + <skip /> + <!-- no translation found for permdesc_readLogs (8896449437464867766) --> + <skip /> <string name="permlab_diagnostic" msgid="8076743953908000342">"lettura/scrittura risorse di proprietà di diag"</string> <string name="permdesc_diagnostic" msgid="3121238373951637049">"Consente a un\'applicazione di leggere le risorse del gruppo diag e scrivere a esse, per esempio i file in /dev. Questa capacità potrebbe influire sulla stabilità e sicurezza del sistema. Dovrebbe essere utilizzata SOLTANTO per diagnostiche specifiche dell\'hardware effettuate dal produttore o dall\'operatore."</string> <string name="permlab_changeComponentState" msgid="79425198834329406">"attivazione/disattivazione componenti applicazioni"</string> @@ -434,8 +436,7 @@ <string name="policydesc_wipeData" msgid="2314060933796396205">"Esegui un ripristino di fabbrica, eliminando tutti i tuoi dati senza conferma."</string> <!-- no translation found for policylab_setGlobalProxy (2784828293747791446) --> <skip /> - <!-- no translation found for policydesc_setGlobalProxy (6387497466660154931) --> - <skip /> + <string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"Imposta il proxy globale del dispositivo in modo da utilizzarlo mentre la norma è attiva. Il proxy globale effettivo è impostabile solo dal primo amministratore del dispositivo."</string> <string-array name="phoneTypes"> <item msgid="8901098336658710359">"Casa"</item> <item msgid="869923650527136615">"Cellulare"</item> @@ -529,6 +530,14 @@ <string name="orgTypeWork" msgid="29268870505363872">"Lavoro"</string> <string name="orgTypeOther" msgid="3951781131570124082">"Altro"</string> <string name="orgTypeCustom" msgid="225523415372088322">"Personalizzato"</string> + <!-- no translation found for sipAddressTypeCustom (2473580593111590945) --> + <skip /> + <!-- no translation found for sipAddressTypeHome (6093598181069359295) --> + <skip /> + <!-- no translation found for sipAddressTypeWork (6920725730797099047) --> + <skip /> + <!-- no translation found for sipAddressTypeOther (4408436162950119849) --> + <skip /> <string name="keyguard_password_enter_pin_code" msgid="3731488827218876115">"Inserisci il PIN"</string> <string name="keyguard_password_enter_password_code" msgid="9138158344813213754">"Inserisci password per sbloccare"</string> <string name="keyguard_password_enter_pin_password_code" msgid="638347075625491514">"Inserisci PIN per sbloccare"</string> @@ -589,6 +598,8 @@ <string name="js_dialog_before_unload" msgid="1901675448179653089">"Uscire da questa pagina?"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n"Seleziona OK per continuare o Annulla per rimanere nella pagina corrente."</string> <string name="save_password_label" msgid="6860261758665825069">"Conferma"</string> <string name="double_tap_toast" msgid="1068216937244567247">"Suggerimento. Tocca due volte per aumentare/ridurre lo zoom."</string> + <!-- no translation found for autofill_this_form (8940110866775097494) --> + <skip /> <string name="permlab_readHistoryBookmarks" msgid="1284843728203412135">"lettura cronologia e segnalibri del browser"</string> <string name="permdesc_readHistoryBookmarks" msgid="4981489815467617191">"Consente all\'applicazione di leggere tutti gli URL visitati e tutti i segnalibri del browser."</string> <string name="permlab_writeHistoryBookmarks" msgid="9009434109836280374">"creazione cronologia e segnalibri del browser"</string> @@ -875,10 +886,10 @@ <string name="throttle_warning_notification_message" msgid="2609734763845705708">"Tocca per informazioni sull\'utilizzo dati cell."</string> <string name="throttled_notification_title" msgid="6269541897729781332">"Limite dati cell. superato"</string> <string name="throttled_notification_message" msgid="4712369856601275146">"Tocca per informazioni sull\'utilizzo dati cell."</string> - <!-- no translation found for no_matches (8129421908915840737) --> - <skip /> - <!-- no translation found for find_on_page (1946799233822820384) --> - <skip /> - <!-- no translation found for matches_found:one (8167147081136579439) --> - <!-- no translation found for matches_found:other (4641872797067609177) --> + <string name="no_matches" msgid="8129421908915840737">"Nessuna corrispondenza"</string> + <string name="find_on_page" msgid="1946799233822820384">"Trova nella pagina"</string> + <plurals name="matches_found"> + <item quantity="one" msgid="8167147081136579439">"1 corrispondenza"</item> + <item quantity="other" msgid="4641872797067609177">"<xliff:g id="INDEX">%d</xliff:g> di <xliff:g id="TOTAL">%d</xliff:g>"</item> + </plurals> </resources> diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml index ea9bf9f..6ad7a75 100644 --- a/core/res/res/values-ja/strings.xml +++ b/core/res/res/values-ja/strings.xml @@ -266,8 +266,10 @@ <string name="permdesc_clearAppCache" msgid="7740465694193671402">"アプリケーションのキャッシュディレクトリからファイルを削除して携帯電話のメモリを解放することをアプリケーションに許可します。通常、アクセスはシステムプロセスのみに制限されます。"</string> <string name="permlab_movePackage" msgid="728454979946503926">"アプリケーションリソースの移動"</string> <string name="permdesc_movePackage" msgid="6323049291923925277">"内部と外部のメディア間でのアプリケーションリソースの移動をアプリケーションに許可します。"</string> - <string name="permlab_readLogs" msgid="4811921703882532070">"システムログファイルの読み取り"</string> - <string name="permdesc_readLogs" msgid="2257937955580475902">"システムのさまざまなログファイルの読み取りをアプリケーションに許可します。これにより携帯電話の使用状況に関する全般情報が取得されますが、個人情報や非公開情報が含まれることはありません。"</string> + <!-- no translation found for permlab_readLogs (6615778543198967614) --> + <skip /> + <!-- no translation found for permdesc_readLogs (8896449437464867766) --> + <skip /> <string name="permlab_diagnostic" msgid="8076743953908000342">"diagが所有するリソースの読み書き"</string> <string name="permdesc_diagnostic" msgid="3121238373951637049">"diagグループが所有するリソース(例:/dev内のファイル)への読み書きをアプリケーションに許可します。システムの安定性とセキュリティに影響する恐れがあります。メーカー/オペレーターによるハードウェア固有の診断以外には使用しないでください。"</string> <string name="permlab_changeComponentState" msgid="79425198834329406">"アプリケーションのコンポーネントを有効/無効にする"</string> @@ -434,8 +436,7 @@ <string name="policydesc_wipeData" msgid="2314060933796396205">"出荷時設定にリセットします。確認なしでデータがすべて削除されます。"</string> <!-- no translation found for policylab_setGlobalProxy (2784828293747791446) --> <skip /> - <!-- no translation found for policydesc_setGlobalProxy (6387497466660154931) --> - <skip /> + <string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"ポリシーが有効になっている場合は端末のグローバルプロキシが使用されるように設定します。有効なグローバルプロキシを設定できるのは最初のデバイス管理者だけです。"</string> <string-array name="phoneTypes"> <item msgid="8901098336658710359">"自宅"</item> <item msgid="869923650527136615">"携帯"</item> @@ -529,6 +530,14 @@ <string name="orgTypeWork" msgid="29268870505363872">"勤務先"</string> <string name="orgTypeOther" msgid="3951781131570124082">"その他"</string> <string name="orgTypeCustom" msgid="225523415372088322">"カスタム"</string> + <!-- no translation found for sipAddressTypeCustom (2473580593111590945) --> + <skip /> + <!-- no translation found for sipAddressTypeHome (6093598181069359295) --> + <skip /> + <!-- no translation found for sipAddressTypeWork (6920725730797099047) --> + <skip /> + <!-- no translation found for sipAddressTypeOther (4408436162950119849) --> + <skip /> <string name="keyguard_password_enter_pin_code" msgid="3731488827218876115">"PINコードを入力"</string> <string name="keyguard_password_enter_password_code" msgid="9138158344813213754">"ロックを解除するにはパスワードを入力"</string> <string name="keyguard_password_enter_pin_password_code" msgid="638347075625491514">"ロックを解除するにはPINを入力"</string> @@ -589,6 +598,8 @@ <string name="js_dialog_before_unload" msgid="1901675448179653089">"このページから移動しますか?"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n"移動する場合は[OK]、今のページに残る場合は[キャンセル]を選択してください。"</string> <string name="save_password_label" msgid="6860261758665825069">"確認"</string> <string name="double_tap_toast" msgid="1068216937244567247">"ヒント: ダブルタップで拡大/縮小できます。"</string> + <!-- no translation found for autofill_this_form (8940110866775097494) --> + <skip /> <string name="permlab_readHistoryBookmarks" msgid="1284843728203412135">"ブラウザの履歴とブックマークを読み取る"</string> <string name="permdesc_readHistoryBookmarks" msgid="4981489815467617191">"ブラウザでアクセスしたすべてのURLおよびブラウザのすべてのブックマークの読み取りをアプリケーションに許可します。"</string> <string name="permlab_writeHistoryBookmarks" msgid="9009434109836280374">"ブラウザの履歴とブックマークを書き込む"</string> @@ -875,10 +886,10 @@ <string name="throttle_warning_notification_message" msgid="2609734763845705708">"タップしてモバイルデータ利用の詳細を表示します"</string> <string name="throttled_notification_title" msgid="6269541897729781332">"モバイルデータの制限を超えました"</string> <string name="throttled_notification_message" msgid="4712369856601275146">"タップしてモバイルデータ利用の詳細を表示します"</string> - <!-- no translation found for no_matches (8129421908915840737) --> - <skip /> - <!-- no translation found for find_on_page (1946799233822820384) --> - <skip /> - <!-- no translation found for matches_found:one (8167147081136579439) --> - <!-- no translation found for matches_found:other (4641872797067609177) --> + <string name="no_matches" msgid="8129421908915840737">"該当なし"</string> + <string name="find_on_page" msgid="1946799233822820384">"ページ内を検索"</string> + <plurals name="matches_found"> + <item quantity="one" msgid="8167147081136579439">"1件一致"</item> + <item quantity="other" msgid="4641872797067609177">"<xliff:g id="INDEX">%d</xliff:g>/<xliff:g id="TOTAL">%d</xliff:g>件"</item> + </plurals> </resources> diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml index a2309da..3b9bd3f 100644 --- a/core/res/res/values-ko/strings.xml +++ b/core/res/res/values-ko/strings.xml @@ -266,8 +266,10 @@ <string name="permdesc_clearAppCache" msgid="7740465694193671402">"애플리케이션이 애플리케이션 캐시 디렉토리에 있는 파일을 삭제하여 휴대전화의 저장공간을 늘릴 수 있도록 합니다. 액세스는 일반적으로 시스템 프로세스로 제한됩니다."</string> <string name="permlab_movePackage" msgid="728454979946503926">"애플리케이션 리소스 이동"</string> <string name="permdesc_movePackage" msgid="6323049291923925277">"애플리케이션이 애플리케이션 리소스를 내부에서 외부 미디어로 또는 그 반대로 이동할 수 있도록 합니다."</string> - <string name="permlab_readLogs" msgid="4811921703882532070">"시스템 로그 파일 읽기"</string> - <string name="permdesc_readLogs" msgid="2257937955580475902">"애플리케이션이 시스템의 다양한 로그 파일을 읽을 수 있도록 합니다. 이 경우 애플리케이션은 사용자가 휴대전화로 수행하는 작업에 대한 일반적인 정보를 검색할 수 있습니다. 하지만 로그 파일에 어떠한 개인정보도 포함되어서는 안 됩니다."</string> + <!-- no translation found for permlab_readLogs (6615778543198967614) --> + <skip /> + <!-- no translation found for permdesc_readLogs (8896449437464867766) --> + <skip /> <string name="permlab_diagnostic" msgid="8076743953908000342">"진단 그룹 소유의 리소스 읽기/쓰기"</string> <string name="permdesc_diagnostic" msgid="3121238373951637049">"애플리케이션이 진단 그룹 소유의 리소스(예: /dev에 있는 파일)를 읽고 쓸 수 있도록 합니다. 이 기능은 시스템 안정성 및 보안에 영향을 미칠 수 있으므로 제조업체 또는 사업자가 하드웨어 관련 진단을 수행하는 경우에만 사용해야 합니다."</string> <string name="permlab_changeComponentState" msgid="79425198834329406">"애플리케이션 구성 요소 사용 또는 사용 안함"</string> @@ -434,8 +436,7 @@ <string name="policydesc_wipeData" msgid="2314060933796396205">"초기화를 수행하여 모든 데이터를 확인하지 않고 삭제합니다."</string> <!-- no translation found for policylab_setGlobalProxy (2784828293747791446) --> <skip /> - <!-- no translation found for policydesc_setGlobalProxy (6387497466660154931) --> - <skip /> + <string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"정책이 사용 설정되어 있는 동안 사용될 기기 글로벌 프록시를 설정합니다. 첫 번째 기기 관리자가 설정한 글로벌 프록시만 유효합니다."</string> <string-array name="phoneTypes"> <item msgid="8901098336658710359">"집"</item> <item msgid="869923650527136615">"모바일"</item> @@ -529,6 +530,14 @@ <string name="orgTypeWork" msgid="29268870505363872">"직장"</string> <string name="orgTypeOther" msgid="3951781131570124082">"기타"</string> <string name="orgTypeCustom" msgid="225523415372088322">"맞춤설정"</string> + <!-- no translation found for sipAddressTypeCustom (2473580593111590945) --> + <skip /> + <!-- no translation found for sipAddressTypeHome (6093598181069359295) --> + <skip /> + <!-- no translation found for sipAddressTypeWork (6920725730797099047) --> + <skip /> + <!-- no translation found for sipAddressTypeOther (4408436162950119849) --> + <skip /> <string name="keyguard_password_enter_pin_code" msgid="3731488827218876115">"PIN 코드 입력"</string> <string name="keyguard_password_enter_password_code" msgid="9138158344813213754">"잠금을 해제하려면 비밀번호 입력"</string> <string name="keyguard_password_enter_pin_password_code" msgid="638347075625491514">"잠금을 해제하려면 PIN 입력"</string> @@ -589,6 +598,8 @@ <string name="js_dialog_before_unload" msgid="1901675448179653089">"다른 페이지를 탐색하시겠습니까?"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n"계속하려면 \'확인\'을 선택하고 현재 페이지에 그대로 있으려면 \'취소\'를 선택하세요."</string> <string name="save_password_label" msgid="6860261758665825069">"확인"</string> <string name="double_tap_toast" msgid="1068216937244567247">"도움말: 축소/확대하려면 두 번 누릅니다."</string> + <!-- no translation found for autofill_this_form (8940110866775097494) --> + <skip /> <string name="permlab_readHistoryBookmarks" msgid="1284843728203412135">"브라우저의 기록 및 북마크 읽기"</string> <string name="permdesc_readHistoryBookmarks" msgid="4981489815467617191">"애플리케이션이 브라우저로 방문한 모든 URL과 브라우저의 모든 북마크를 읽도록 허용합니다."</string> <string name="permlab_writeHistoryBookmarks" msgid="9009434109836280374">"브라우저의 기록 및 북마크 쓰기"</string> @@ -875,10 +886,10 @@ <string name="throttle_warning_notification_message" msgid="2609734763845705708">"모바일 데이터 사용에 대해 자세히 알아보려면 터치하세요."</string> <string name="throttled_notification_title" msgid="6269541897729781332">"모바일 데이터 제한을 초과했습니다."</string> <string name="throttled_notification_message" msgid="4712369856601275146">"모바일 데이터 사용에 대해 자세히 알아보려면 터치하세요."</string> - <!-- no translation found for no_matches (8129421908915840737) --> - <skip /> - <!-- no translation found for find_on_page (1946799233822820384) --> - <skip /> - <!-- no translation found for matches_found:one (8167147081136579439) --> - <!-- no translation found for matches_found:other (4641872797067609177) --> + <string name="no_matches" msgid="8129421908915840737">"검색결과 없음"</string> + <string name="find_on_page" msgid="1946799233822820384">"페이지에서 찾기"</string> + <plurals name="matches_found"> + <item quantity="one" msgid="8167147081136579439">"검색결과 1개"</item> + <item quantity="other" msgid="4641872797067609177">"<xliff:g id="INDEX">%d</xliff:g>/<xliff:g id="TOTAL">%d</xliff:g>"</item> + </plurals> </resources> diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml index 43e5597..831e183 100644 --- a/core/res/res/values-nb/strings.xml +++ b/core/res/res/values-nb/strings.xml @@ -266,8 +266,10 @@ <string name="permdesc_clearAppCache" msgid="7740465694193671402">"Lar applikasjonen frigjøre lagringsplass ved å slette filer i applikasjoners hurtigbufferkatalog. Tilgangen er vanligvis sterkt begrenset, til systemprosesser."</string> <string name="permlab_movePackage" msgid="728454979946503926">"Flytter programressurser"</string> <string name="permdesc_movePackage" msgid="6323049291923925277">"Gir et program tillatelse til å flytte programressurser fra interne til eksterne medier og omvendt."</string> - <string name="permlab_readLogs" msgid="4811921703882532070">"lese systemets loggfiler"</string> - <string name="permdesc_readLogs" msgid="2257937955580475902">"Lar applikasjonen lese fra diverse loggfiler på systemet. Disse inneholder generell informasjon om hva som gjøres med telefonen, men skal ikke inneholde personlig eller privat informasjon."</string> + <!-- no translation found for permlab_readLogs (6615778543198967614) --> + <skip /> + <!-- no translation found for permdesc_readLogs (8896449437464867766) --> + <skip /> <string name="permlab_diagnostic" msgid="8076743953908000342">"lese/skrive ressurser eid av diag"</string> <string name="permdesc_diagnostic" msgid="3121238373951637049">"Lar applikasjonen lese og skrive enhver ressurs eid av gruppen diag; for eksempel, filer i /dev. Dette kan potensielt påvirke systemets sikkerhet og stabilitet. Dette bør KUN brukes for maskinvarespesifikke diagnoseverktøy laget av operatøren eller produsenten."</string> <string name="permlab_changeComponentState" msgid="79425198834329406">"aktivere eller deaktigere applikasjonskomponenter"</string> @@ -434,8 +436,7 @@ <string name="policydesc_wipeData" msgid="2314060933796396205">"Utfører tilbakestilling til fabrikkstandard. Alle data slettes uten varsel."</string> <!-- no translation found for policylab_setGlobalProxy (2784828293747791446) --> <skip /> - <!-- no translation found for policydesc_setGlobalProxy (6387497466660154931) --> - <skip /> + <string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"Angir den globale mellomtjeneren på enheten som skal brukes når regelen er aktivert. Kun den opprinnelige administratoren av enheten kan angi den globale mellomtjeneren."</string> <string-array name="phoneTypes"> <item msgid="8901098336658710359">"Hjemmenummer"</item> <item msgid="869923650527136615">"Mobil"</item> @@ -529,6 +530,14 @@ <string name="orgTypeWork" msgid="29268870505363872">"Arbeid"</string> <string name="orgTypeOther" msgid="3951781131570124082">"Annen"</string> <string name="orgTypeCustom" msgid="225523415372088322">"Egendefinert"</string> + <!-- no translation found for sipAddressTypeCustom (2473580593111590945) --> + <skip /> + <!-- no translation found for sipAddressTypeHome (6093598181069359295) --> + <skip /> + <!-- no translation found for sipAddressTypeWork (6920725730797099047) --> + <skip /> + <!-- no translation found for sipAddressTypeOther (4408436162950119849) --> + <skip /> <string name="keyguard_password_enter_pin_code" msgid="3731488827218876115">"Skriv inn PIN-kode:"</string> <string name="keyguard_password_enter_password_code" msgid="9138158344813213754">"Skriv inn passord for å låse opp"</string> <string name="keyguard_password_enter_pin_password_code" msgid="638347075625491514">"Skriv inn personlig kode for å låse opp"</string> @@ -589,6 +598,8 @@ <string name="js_dialog_before_unload" msgid="1901675448179653089">"Naviger bort fra denne siden?"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n"Velg OK for å fortsette, eller Avbryt for å forbli på denne siden."</string> <string name="save_password_label" msgid="6860261758665825069">"Bekreft"</string> <string name="double_tap_toast" msgid="1068216937244567247">"Dobbelttrykk for å zoome inn og ut."</string> + <!-- no translation found for autofill_this_form (8940110866775097494) --> + <skip /> <string name="permlab_readHistoryBookmarks" msgid="1284843728203412135">"lese nettleserens logg og bokmerker"</string> <string name="permdesc_readHistoryBookmarks" msgid="4981489815467617191">"Lar applikasjonen lese alle adresser nettleseren har besøkt, og alle nettleserens bokmerker."</string> <string name="permlab_writeHistoryBookmarks" msgid="9009434109836280374">"skrive til nettleserens logg og bokmerker"</string> @@ -875,10 +886,10 @@ <string name="throttle_warning_notification_message" msgid="2609734763845705708">"Berør for å lese mer om bruk av mobildata"</string> <string name="throttled_notification_title" msgid="6269541897729781332">"Grensen for mobildatabruk er overskredet"</string> <string name="throttled_notification_message" msgid="4712369856601275146">"Berør for å lese mer om bruk av mobildata"</string> - <!-- no translation found for no_matches (8129421908915840737) --> - <skip /> - <!-- no translation found for find_on_page (1946799233822820384) --> - <skip /> - <!-- no translation found for matches_found:one (8167147081136579439) --> - <!-- no translation found for matches_found:other (4641872797067609177) --> + <string name="no_matches" msgid="8129421908915840737">"Ingen treff"</string> + <string name="find_on_page" msgid="1946799233822820384">"Finn på side"</string> + <plurals name="matches_found"> + <item quantity="one" msgid="8167147081136579439">"1 treff"</item> + <item quantity="other" msgid="4641872797067609177">"<xliff:g id="INDEX">%d</xliff:g> av <xliff:g id="TOTAL">%d</xliff:g>"</item> + </plurals> </resources> diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml index 7aded96..28fdcc1 100644 --- a/core/res/res/values-nl/strings.xml +++ b/core/res/res/values-nl/strings.xml @@ -266,8 +266,10 @@ <string name="permdesc_clearAppCache" msgid="7740465694193671402">"Hiermee kan een toepassing opslagruimte op de telefoon vrij maken door bestanden te verwijderen uit de cachemap van de toepassing. De toegang is doorgaans beperkt tot het systeemproces."</string> <string name="permlab_movePackage" msgid="728454979946503926">"Toepassingsbronnen verplaatsen"</string> <string name="permdesc_movePackage" msgid="6323049291923925277">"Een toepassing toestaan toepassingsbronnen te verplaatsen van interne naar externe media en omgekeerd."</string> - <string name="permlab_readLogs" msgid="4811921703882532070">"systeemlogbestanden lezen"</string> - <string name="permdesc_readLogs" msgid="2257937955580475902">"Hiermee kan een toepassing de verschillende logbestanden van het systeem lezen. De toepassing kan op deze manier algemene informatie achterhalen over uw telefoongebruik. Hierin is geen persoonlijke of privé-informatie opgenomen."</string> + <!-- no translation found for permlab_readLogs (6615778543198967614) --> + <skip /> + <!-- no translation found for permdesc_readLogs (8896449437464867766) --> + <skip /> <string name="permlab_diagnostic" msgid="8076743953908000342">"lezen/schrijven naar bronnen van diag"</string> <string name="permdesc_diagnostic" msgid="3121238373951637049">"Hiermee kan een toepassing lezen en schrijven naar elke bron die hoort bij de diagnostische groep, zoals bestanden in /dev. Hierdoor kan de systeemstabiliteit en -veiligheid worden beïnvloed. Dit mag ALLEEN worden gebruikt voor hardwarespecifieke diagnostiek door de fabrikant of operator."</string> <string name="permlab_changeComponentState" msgid="79425198834329406">"toepassingscomponenten in- of uitschakelen"</string> @@ -434,8 +436,7 @@ <string name="policydesc_wipeData" msgid="2314060933796396205">"De fabrieksinstellingen herstellen, waarbij alle gegevens worden verwijderd zonder bevestiging."</string> <!-- no translation found for policylab_setGlobalProxy (2784828293747791446) --> <skip /> - <!-- no translation found for policydesc_setGlobalProxy (6387497466660154931) --> - <skip /> + <string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"Stel de algemene proxy voor het apparaat in die moet worden gebruikt terwijl het beleid is geactiveerd. Alleen de eerste apparaatbeheerder stelt de algemene proxy is."</string> <string-array name="phoneTypes"> <item msgid="8901098336658710359">"Thuis"</item> <item msgid="869923650527136615">"Mobiel"</item> @@ -529,6 +530,14 @@ <string name="orgTypeWork" msgid="29268870505363872">"Werk"</string> <string name="orgTypeOther" msgid="3951781131570124082">"Overig"</string> <string name="orgTypeCustom" msgid="225523415372088322">"Aangepast"</string> + <!-- no translation found for sipAddressTypeCustom (2473580593111590945) --> + <skip /> + <!-- no translation found for sipAddressTypeHome (6093598181069359295) --> + <skip /> + <!-- no translation found for sipAddressTypeWork (6920725730797099047) --> + <skip /> + <!-- no translation found for sipAddressTypeOther (4408436162950119849) --> + <skip /> <string name="keyguard_password_enter_pin_code" msgid="3731488827218876115">"PIN-code invoeren"</string> <string name="keyguard_password_enter_password_code" msgid="9138158344813213754">"Voer het wachtwoord in om te ontgrendelen"</string> <string name="keyguard_password_enter_pin_password_code" msgid="638347075625491514">"Voer de PIN-code in om te ontgrendelen"</string> @@ -589,6 +598,8 @@ <string name="js_dialog_before_unload" msgid="1901675448179653089">"Wilt u deze pagina verlaten?"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n"Kies OK om door te gaan of Annuleren om op de huidige pagina te blijven."</string> <string name="save_password_label" msgid="6860261758665825069">"Bevestigen"</string> <string name="double_tap_toast" msgid="1068216937244567247">"Tip: tik tweemaal om in of uit te zoomen."</string> + <!-- no translation found for autofill_this_form (8940110866775097494) --> + <skip /> <string name="permlab_readHistoryBookmarks" msgid="1284843728203412135">"browsergeschiedenis en bladwijzers lezen"</string> <string name="permdesc_readHistoryBookmarks" msgid="4981489815467617191">"Hiermee kan een toepassing de URL\'s lezen die u via de browser heeft bezocht, evenals alle bladwijzers van de browser."</string> <string name="permlab_writeHistoryBookmarks" msgid="9009434109836280374">"browsergeschiedenis en bladwijzers schrijven"</string> @@ -875,10 +886,10 @@ <string name="throttle_warning_notification_message" msgid="2609734763845705708">"Raak aan voor meer informatie over mobiel gegevensgebruik"</string> <string name="throttled_notification_title" msgid="6269541897729781332">"Mobiele gegevenslimiet overschreden"</string> <string name="throttled_notification_message" msgid="4712369856601275146">"Raak aan voor meer informatie over mobiel gegevensgebruik"</string> - <!-- no translation found for no_matches (8129421908915840737) --> - <skip /> - <!-- no translation found for find_on_page (1946799233822820384) --> - <skip /> - <!-- no translation found for matches_found:one (8167147081136579439) --> - <!-- no translation found for matches_found:other (4641872797067609177) --> + <string name="no_matches" msgid="8129421908915840737">"Geen overeenkomsten"</string> + <string name="find_on_page" msgid="1946799233822820384">"Zoeken op pagina"</string> + <plurals name="matches_found"> + <item quantity="one" msgid="8167147081136579439">"1 overeenkomst"</item> + <item quantity="other" msgid="4641872797067609177">"<xliff:g id="INDEX">%d</xliff:g> van <xliff:g id="TOTAL">%d</xliff:g>"</item> + </plurals> </resources> diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml index c6a0d9c..33b03ad 100644 --- a/core/res/res/values-pl/strings.xml +++ b/core/res/res/values-pl/strings.xml @@ -266,8 +266,10 @@ <string name="permdesc_clearAppCache" msgid="7740465694193671402">"Pozwala aplikacji na zwalnianie pamięci telefonu przez usuwanie plików z katalogu pamięci podręcznej aplikacji. Dostęp jest bardzo ograniczony, z reguły do procesów systemu."</string> <string name="permlab_movePackage" msgid="728454979946503926">"Przenoszenie zasobów aplikacji"</string> <string name="permdesc_movePackage" msgid="6323049291923925277">"Zezwala aplikacji na przeniesienie zasobów aplikacji z nośnika wewnętrznego na zewnętrzny i odwrotnie."</string> - <string name="permlab_readLogs" msgid="4811921703882532070">"czytanie plików dziennika systemu"</string> - <string name="permdesc_readLogs" msgid="2257937955580475902">"Umożliwia aplikacji czytanie różnych plików dziennika systemowego. Pozwala to na uzyskanie ogólnych informacji o czynnościach wykonywanych w telefonie, ale bez ujawniania danych osobowych lub osobistych informacji."</string> + <!-- no translation found for permlab_readLogs (6615778543198967614) --> + <skip /> + <!-- no translation found for permdesc_readLogs (8896449437464867766) --> + <skip /> <string name="permlab_diagnostic" msgid="8076743953908000342">"czytanie/zapisywanie w zasobach należących do diagnostyki"</string> <string name="permdesc_diagnostic" msgid="3121238373951637049">"Pozwala aplikacji na czytanie i zapisywanie we wszystkich zasobach posiadanych przez diagnozowaną grupę, jak na przykład pliki w katalogu /dev. Może to potencjalnie wpłynąć na stabilność i bezpieczeństwo systemu. Powinno być wykorzystywane TYLKO w celach diagnozowania sprzętu przez producenta lub operatora."</string> <string name="permlab_changeComponentState" msgid="79425198834329406">"włączanie lub wyłączanie składników aplikacji"</string> @@ -434,8 +436,7 @@ <string name="policydesc_wipeData" msgid="2314060933796396205">"Wykonuje reset fabryczny, usuwając wszystkie dane użytkownika bez żadnego potwierdzenia."</string> <!-- no translation found for policylab_setGlobalProxy (2784828293747791446) --> <skip /> - <!-- no translation found for policydesc_setGlobalProxy (6387497466660154931) --> - <skip /> + <string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"Ustaw globalny serwer proxy urządzenia do używania przy włączonych zasadach. Tylko pierwszy administrator urządzenia ustawia obowiązujący globalny serwer proxy."</string> <string-array name="phoneTypes"> <item msgid="8901098336658710359">"Dom"</item> <item msgid="869923650527136615">"Komórka"</item> @@ -529,6 +530,14 @@ <string name="orgTypeWork" msgid="29268870505363872">"Służbowy"</string> <string name="orgTypeOther" msgid="3951781131570124082">"Inny"</string> <string name="orgTypeCustom" msgid="225523415372088322">"Niestandardowy"</string> + <!-- no translation found for sipAddressTypeCustom (2473580593111590945) --> + <skip /> + <!-- no translation found for sipAddressTypeHome (6093598181069359295) --> + <skip /> + <!-- no translation found for sipAddressTypeWork (6920725730797099047) --> + <skip /> + <!-- no translation found for sipAddressTypeOther (4408436162950119849) --> + <skip /> <string name="keyguard_password_enter_pin_code" msgid="3731488827218876115">"Wprowadź kod PIN"</string> <string name="keyguard_password_enter_password_code" msgid="9138158344813213754">"Wprowadź hasło, aby odblokować"</string> <string name="keyguard_password_enter_pin_password_code" msgid="638347075625491514">"Wprowadź kod PIN, aby odblokować"</string> @@ -589,6 +598,8 @@ <string name="js_dialog_before_unload" msgid="1901675448179653089">"Czy opuścić tę stronę?"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n"Wybierz opcję OK, aby kontynuować, lub opcję Anuluj, aby pozostać na tej stronie."</string> <string name="save_password_label" msgid="6860261758665825069">"Potwierdź"</string> <string name="double_tap_toast" msgid="1068216937244567247">"Wskazówka: dotknij dwukrotnie, aby powiększyć lub pomniejszyć."</string> + <!-- no translation found for autofill_this_form (8940110866775097494) --> + <skip /> <string name="permlab_readHistoryBookmarks" msgid="1284843728203412135">"odczyt historii i zakładek przeglądarki"</string> <string name="permdesc_readHistoryBookmarks" msgid="4981489815467617191">"Umożliwia aplikacji odczyt wszystkich adresów URL odwiedzonych przez przeglądarkę, a także wszystkich zakładek przeglądarki."</string> <string name="permlab_writeHistoryBookmarks" msgid="9009434109836280374">"zapis historii i zakładek przeglądarki"</string> @@ -875,10 +886,10 @@ <string name="throttle_warning_notification_message" msgid="2609734763845705708">"Dotknij, aby zobaczyć statystyki przesyłu danych"</string> <string name="throttled_notification_title" msgid="6269541897729781332">"Przekroczono limit danych"</string> <string name="throttled_notification_message" msgid="4712369856601275146">"Dotknij, aby zobaczyć statystyki przesyłu danych"</string> - <!-- no translation found for no_matches (8129421908915840737) --> - <skip /> - <!-- no translation found for find_on_page (1946799233822820384) --> - <skip /> - <!-- no translation found for matches_found:one (8167147081136579439) --> - <!-- no translation found for matches_found:other (4641872797067609177) --> + <string name="no_matches" msgid="8129421908915840737">"Brak wyników"</string> + <string name="find_on_page" msgid="1946799233822820384">"Znajdź na stronie"</string> + <plurals name="matches_found"> + <item quantity="one" msgid="8167147081136579439">"1 wynik"</item> + <item quantity="other" msgid="4641872797067609177">"<xliff:g id="INDEX">%d</xliff:g> z <xliff:g id="TOTAL">%d</xliff:g>"</item> + </plurals> </resources> diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml index 638da34..dc762d1 100644 --- a/core/res/res/values-pt-rPT/strings.xml +++ b/core/res/res/values-pt-rPT/strings.xml @@ -266,8 +266,10 @@ <string name="permdesc_clearAppCache" msgid="7740465694193671402">"Permite a uma aplicação libertar espaço de armazenamento no telefone eliminando ficheiros no directório da cache da aplicação. Geralmente, o acesso é muito limitado para processamento do sistema."</string> <string name="permlab_movePackage" msgid="728454979946503926">"Mover recursos de aplicações"</string> <string name="permdesc_movePackage" msgid="6323049291923925277">"Permite que uma aplicação mova recursos de aplicações de meios internos para meios externos e vice-versa."</string> - <string name="permlab_readLogs" msgid="4811921703882532070">"ler ficheiros de registo do sistema"</string> - <string name="permdesc_readLogs" msgid="2257937955580475902">"Permite a uma aplicação ler a partir dos diversos ficheiros de registo do sistema. Isto permite descobrir informações gerais sobre a forma como o utilizador usa o telefone, mas estas não devem conter quaisquer dados pessoais ou privados."</string> + <!-- no translation found for permlab_readLogs (6615778543198967614) --> + <skip /> + <!-- no translation found for permdesc_readLogs (8896449437464867766) --> + <skip /> <string name="permlab_diagnostic" msgid="8076743953908000342">"ler/escrever em recursos propriedade de diag"</string> <string name="permdesc_diagnostic" msgid="3121238373951637049">"Permite a uma aplicação ler e escrever em qualquer recurso que seja propriedade do grupo diag. Por exemplo, ficheiros em /dev. Isto pode afectar potencialmente a estabilidade e a segurança do sistema e deve ser utilizado APENAS para diagnósticos específicos do hardware pelo fabricante ou pelo operador."</string> <string name="permlab_changeComponentState" msgid="79425198834329406">"activar ou desactivar componentes da aplicação"</string> @@ -434,8 +436,7 @@ <string name="policydesc_wipeData" msgid="2314060933796396205">"Efectue uma reposição de fábrica, eliminando todos os dados sem qualquer confirmação."</string> <!-- no translation found for policylab_setGlobalProxy (2784828293747791446) --> <skip /> - <!-- no translation found for policydesc_setGlobalProxy (6387497466660154931) --> - <skip /> + <string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"Definir o proxy global do dispositivo a ser utilizado quando a política estiver activada. Só o primeiro administrador do dispositivo define o proxy global efectivo."</string> <string-array name="phoneTypes"> <item msgid="8901098336658710359">"Residência"</item> <item msgid="869923650527136615">"Móvel"</item> @@ -529,6 +530,14 @@ <string name="orgTypeWork" msgid="29268870505363872">"Emprego"</string> <string name="orgTypeOther" msgid="3951781131570124082">"Outro"</string> <string name="orgTypeCustom" msgid="225523415372088322">"Personalizado"</string> + <!-- no translation found for sipAddressTypeCustom (2473580593111590945) --> + <skip /> + <!-- no translation found for sipAddressTypeHome (6093598181069359295) --> + <skip /> + <!-- no translation found for sipAddressTypeWork (6920725730797099047) --> + <skip /> + <!-- no translation found for sipAddressTypeOther (4408436162950119849) --> + <skip /> <string name="keyguard_password_enter_pin_code" msgid="3731488827218876115">"Introduzir código PIN"</string> <string name="keyguard_password_enter_password_code" msgid="9138158344813213754">"Introduza a palavra-passe para desbloquear"</string> <string name="keyguard_password_enter_pin_password_code" msgid="638347075625491514">"Introduza o PIN para desbloquear"</string> @@ -589,6 +598,8 @@ <string name="js_dialog_before_unload" msgid="1901675448179653089">"Navegar para outra página?"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n"Seleccione OK para continuar ou Cancelar para permanecer na página actual."</string> <string name="save_password_label" msgid="6860261758665825069">"Confirmar"</string> <string name="double_tap_toast" msgid="1068216937244567247">"Sugestão: toque duas vezes para aumentar ou diminuir o zoom."</string> + <!-- no translation found for autofill_this_form (8940110866775097494) --> + <skip /> <string name="permlab_readHistoryBookmarks" msgid="1284843728203412135">"ler histórico e marcadores do browser"</string> <string name="permdesc_readHistoryBookmarks" msgid="4981489815467617191">"Permite que a aplicação leia todos os URLs visitados pelo browser e todos os marcadores do browser."</string> <string name="permlab_writeHistoryBookmarks" msgid="9009434109836280374">"gravar histórico e marcadores do browser"</string> @@ -875,10 +886,10 @@ <string name="throttle_warning_notification_message" msgid="2609734763845705708">"Toque para saber mais sobre a utilização de dados móveis"</string> <string name="throttled_notification_title" msgid="6269541897729781332">"Limite de dados móveis excedido"</string> <string name="throttled_notification_message" msgid="4712369856601275146">"Toque para saber mais sobre a utilização de dados móveis"</string> - <!-- no translation found for no_matches (8129421908915840737) --> - <skip /> - <!-- no translation found for find_on_page (1946799233822820384) --> - <skip /> - <!-- no translation found for matches_found:one (8167147081136579439) --> - <!-- no translation found for matches_found:other (4641872797067609177) --> + <string name="no_matches" msgid="8129421908915840737">"Sem correspondências"</string> + <string name="find_on_page" msgid="1946799233822820384">"Localizar na página"</string> + <plurals name="matches_found"> + <item quantity="one" msgid="8167147081136579439">"1 correspondência"</item> + <item quantity="other" msgid="4641872797067609177">"<xliff:g id="INDEX">%d</xliff:g> de <xliff:g id="TOTAL">%d</xliff:g>"</item> + </plurals> </resources> diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml index 1ec1346..ee038c3 100644 --- a/core/res/res/values-pt/strings.xml +++ b/core/res/res/values-pt/strings.xml @@ -266,8 +266,10 @@ <string name="permdesc_clearAppCache" msgid="7740465694193671402">"Permite que um aplicativo libere o espaço de armazenamento do telefone excluindo arquivos no diretório de cache do aplicativo. O acesso é normalmente muito restrito para o processo do sistema."</string> <string name="permlab_movePackage" msgid="728454979946503926">"Mover recursos do aplicativo"</string> <string name="permdesc_movePackage" msgid="6323049291923925277">"Permite que um aplicativo mova os recursos do aplicativo da mídia interna para a externa e vice-versa."</string> - <string name="permlab_readLogs" msgid="4811921703882532070">"ler arquivos de registro do sistema"</string> - <string name="permdesc_readLogs" msgid="2257937955580475902">"Permite que um aplicativo leia os diversos arquivos de registro do sistema. Isso permite que ele descubra informações gerais sobre o que você está fazendo com o telefone, porém esses arquivos não devem conter informações pessoais ou privadas."</string> + <!-- no translation found for permlab_readLogs (6615778543198967614) --> + <skip /> + <!-- no translation found for permdesc_readLogs (8896449437464867766) --> + <skip /> <string name="permlab_diagnostic" msgid="8076743953908000342">"ler/gravar em recursos pertencentes ao diag"</string> <string name="permdesc_diagnostic" msgid="3121238373951637049">"Permite que um aplicativo leia e grave em qualquer recurso que pertença ao grupo de diagnósticos; por exemplo, arquivos em /dev. Isso possivelmente pode afetar a estabilidade e a segurança do sistema. Isso deve ser usado APENAS para diagnósticos específicos do hardware realizados pelo fabricante ou pelo operador."</string> <string name="permlab_changeComponentState" msgid="79425198834329406">"ativar ou desativar os componentes do aplicativo"</string> @@ -434,8 +436,7 @@ <string name="policydesc_wipeData" msgid="2314060933796396205">"Execute uma redefinição de fábrica, excluindo todos os seus dados sem qualquer confirmação."</string> <!-- no translation found for policylab_setGlobalProxy (2784828293747791446) --> <skip /> - <!-- no translation found for policydesc_setGlobalProxy (6387497466660154931) --> - <skip /> + <string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"Configura o proxy global do dispositivo para ser usado enquanto a política estiver ativada. Somente o primeiro administrador do dispositivo pode configurar um verdadeiro proxy global."</string> <string-array name="phoneTypes"> <item msgid="8901098336658710359">"Residencial"</item> <item msgid="869923650527136615">"Celular"</item> @@ -529,6 +530,14 @@ <string name="orgTypeWork" msgid="29268870505363872">"Comercial"</string> <string name="orgTypeOther" msgid="3951781131570124082">"Outros"</string> <string name="orgTypeCustom" msgid="225523415372088322">"Personalizado"</string> + <!-- no translation found for sipAddressTypeCustom (2473580593111590945) --> + <skip /> + <!-- no translation found for sipAddressTypeHome (6093598181069359295) --> + <skip /> + <!-- no translation found for sipAddressTypeWork (6920725730797099047) --> + <skip /> + <!-- no translation found for sipAddressTypeOther (4408436162950119849) --> + <skip /> <string name="keyguard_password_enter_pin_code" msgid="3731488827218876115">"Digite o código PIN"</string> <string name="keyguard_password_enter_password_code" msgid="9138158344813213754">"Digite a senha para desbloquear"</string> <string name="keyguard_password_enter_pin_password_code" msgid="638347075625491514">"Digite o PIN para desbloquear"</string> @@ -589,6 +598,8 @@ <string name="js_dialog_before_unload" msgid="1901675448179653089">"Deseja sair desta página?"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n"Selecione OK para continuar ou Cancelar para permanecer na página atual."</string> <string name="save_password_label" msgid="6860261758665825069">"Confirmar"</string> <string name="double_tap_toast" msgid="1068216937244567247">"Dica: toque duas vezes para aumentar e diminuir o zoom."</string> + <!-- no translation found for autofill_this_form (8940110866775097494) --> + <skip /> <string name="permlab_readHistoryBookmarks" msgid="1284843728203412135">"ler histórico e favoritos do Navegador"</string> <string name="permdesc_readHistoryBookmarks" msgid="4981489815467617191">"Permite que o aplicativo leia todos os URLs visitados pelo Navegador e todos os favoritos do Navegador."</string> <string name="permlab_writeHistoryBookmarks" msgid="9009434109836280374">"gravar histórico e favoritos do Navegador"</string> @@ -875,10 +886,10 @@ <string name="throttle_warning_notification_message" msgid="2609734763845705708">"Toque para saber mais sobre uso de dados do celular"</string> <string name="throttled_notification_title" msgid="6269541897729781332">"Limite de dados do celular excedido"</string> <string name="throttled_notification_message" msgid="4712369856601275146">"Toque para saber mais sobre o uso de dados do celular"</string> - <!-- no translation found for no_matches (8129421908915840737) --> - <skip /> - <!-- no translation found for find_on_page (1946799233822820384) --> - <skip /> - <!-- no translation found for matches_found:one (8167147081136579439) --> - <!-- no translation found for matches_found:other (4641872797067609177) --> + <string name="no_matches" msgid="8129421908915840737">"Não encontrado"</string> + <string name="find_on_page" msgid="1946799233822820384">"Localizar na página"</string> + <plurals name="matches_found"> + <item quantity="one" msgid="8167147081136579439">"Uma correspondência"</item> + <item quantity="other" msgid="4641872797067609177">"<xliff:g id="INDEX">%d</xliff:g> de <xliff:g id="TOTAL">%d</xliff:g>"</item> + </plurals> </resources> diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml index df2456c..f040bd0 100644 --- a/core/res/res/values-ru/strings.xml +++ b/core/res/res/values-ru/strings.xml @@ -266,8 +266,10 @@ <string name="permdesc_clearAppCache" msgid="7740465694193671402">"Позволяет приложению освобождать память телефона с помощью удаления файлов из каталога кэша приложений. Обычно это разрешается только системным процессам."</string> <string name="permlab_movePackage" msgid="728454979946503926">"Перемещать ресурсы приложения"</string> <string name="permdesc_movePackage" msgid="6323049291923925277">"Позволяет приложению перемещать ресурсы приложения с внутренних на внешние носители и наоборот."</string> - <string name="permlab_readLogs" msgid="4811921703882532070">"считывать системные файлы журналов"</string> - <string name="permdesc_readLogs" msgid="2257937955580475902">"Позволяет приложению считывать информацию из различных журналов системы. Приложение может получить сведения о работе пользователя с телефоном, но они не должны содержать какой-либо личной или конфиденциальной информации."</string> + <!-- no translation found for permlab_readLogs (6615778543198967614) --> + <skip /> + <!-- no translation found for permdesc_readLogs (8896449437464867766) --> + <skip /> <string name="permlab_diagnostic" msgid="8076743953908000342">"считывать/записывать данные в ресурсы, принадлежащие группе диагностики"</string> <string name="permdesc_diagnostic" msgid="3121238373951637049">"Позволяет приложению считывать и записывать данные в любые ресурсы, принадлежащие группе диагностики (например, файлы в каталоге /dev). Это может повлиять на стабильность и безопасность системы. Эта возможность может быть использована ТОЛЬКО производителем или оператором для диагностики аппаратного обеспечения."</string> <string name="permlab_changeComponentState" msgid="79425198834329406">"включать или отключать компоненты приложения"</string> @@ -434,8 +436,7 @@ <string name="policydesc_wipeData" msgid="2314060933796396205">"Выполнить сброс к начальным настройкам с удалением всех данных без запроса подтверждения."</string> <!-- no translation found for policylab_setGlobalProxy (2784828293747791446) --> <skip /> - <!-- no translation found for policydesc_setGlobalProxy (6387497466660154931) --> - <skip /> + <string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"Настройте глобальный прокси-сервер устройства, который будет использоваться при активной политике. Глобальный прокси-сервер должен настроить администратор первого устройства."</string> <string-array name="phoneTypes"> <item msgid="8901098336658710359">"Домашний"</item> <item msgid="869923650527136615">"Мобильный"</item> @@ -529,6 +530,14 @@ <string name="orgTypeWork" msgid="29268870505363872">"Работа"</string> <string name="orgTypeOther" msgid="3951781131570124082">"Другое"</string> <string name="orgTypeCustom" msgid="225523415372088322">"Создать свой ярлык"</string> + <!-- no translation found for sipAddressTypeCustom (2473580593111590945) --> + <skip /> + <!-- no translation found for sipAddressTypeHome (6093598181069359295) --> + <skip /> + <!-- no translation found for sipAddressTypeWork (6920725730797099047) --> + <skip /> + <!-- no translation found for sipAddressTypeOther (4408436162950119849) --> + <skip /> <string name="keyguard_password_enter_pin_code" msgid="3731488827218876115">"Введите PIN-код"</string> <string name="keyguard_password_enter_password_code" msgid="9138158344813213754">"Введите пароль для разблокировки"</string> <string name="keyguard_password_enter_pin_password_code" msgid="638347075625491514">"Введите PIN-код для разблокировки"</string> @@ -589,6 +598,8 @@ <string name="js_dialog_before_unload" msgid="1901675448179653089">"Перейти с этой страницы?"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n"Нажмите \"ОК\", чтобы продолжить, или \"Отмена\", чтобы остаться на текущей странице."</string> <string name="save_password_label" msgid="6860261758665825069">"Подтвердите"</string> <string name="double_tap_toast" msgid="1068216937244567247">"Совет: нажмите дважды, чтобы увеличить и уменьшить масштаб."</string> + <!-- no translation found for autofill_this_form (8940110866775097494) --> + <skip /> <string name="permlab_readHistoryBookmarks" msgid="1284843728203412135">"считывать историю и закладки браузера"</string> <string name="permdesc_readHistoryBookmarks" msgid="4981489815467617191">"Разрешает приложению считывать все URL, посещенные браузером, и все его закладки."</string> <string name="permlab_writeHistoryBookmarks" msgid="9009434109836280374">"записывать историю и закладки браузера"</string> @@ -875,10 +886,10 @@ <string name="throttle_warning_notification_message" msgid="2609734763845705708">"Нажмите, чтобы узнать больше о мобильной передаче данных"</string> <string name="throttled_notification_title" msgid="6269541897729781332">"Превышен лимит на мобильные данные"</string> <string name="throttled_notification_message" msgid="4712369856601275146">"Нажмите, чтобы узнать больше о мобильной передаче данных"</string> - <!-- no translation found for no_matches (8129421908915840737) --> - <skip /> - <!-- no translation found for find_on_page (1946799233822820384) --> - <skip /> - <!-- no translation found for matches_found:one (8167147081136579439) --> - <!-- no translation found for matches_found:other (4641872797067609177) --> + <string name="no_matches" msgid="8129421908915840737">"Нет совпадений"</string> + <string name="find_on_page" msgid="1946799233822820384">"Найти на странице"</string> + <plurals name="matches_found"> + <item quantity="one" msgid="8167147081136579439">"1 совпадение"</item> + <item quantity="other" msgid="4641872797067609177">"<xliff:g id="INDEX">%d</xliff:g> из <xliff:g id="TOTAL">%d</xliff:g>"</item> + </plurals> </resources> diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml index 4ba0e91..418a8e6 100644 --- a/core/res/res/values-sv/strings.xml +++ b/core/res/res/values-sv/strings.xml @@ -266,8 +266,10 @@ <string name="permdesc_clearAppCache" msgid="7740465694193671402">"Tillåter att ett program frigör lagringsutrymme i telefonen genom att ta bort filer i programmets katalog för cachelagring. Åtkomst är mycket begränsad, vanligtvis till systemprocesser."</string> <string name="permlab_movePackage" msgid="728454979946503926">"Flytta programresurser"</string> <string name="permdesc_movePackage" msgid="6323049291923925277">"Tillåter att ett program flyttar programresurser från interna till externa medier och tvärt om."</string> - <string name="permlab_readLogs" msgid="4811921703882532070">"läsa systemets loggfiler"</string> - <string name="permdesc_readLogs" msgid="2257937955580475902">"Tillåter att ett program läser från systemets olika loggfiler. Det innebär att programmet kan upptäcka allmän information om vad du gör med telefonen, men den bör inte innehålla personlig eller privat information."</string> + <!-- no translation found for permlab_readLogs (6615778543198967614) --> + <skip /> + <!-- no translation found for permdesc_readLogs (8896449437464867766) --> + <skip /> <string name="permlab_diagnostic" msgid="8076743953908000342">"läsa/skriva till resurser som ägs av diag"</string> <string name="permdesc_diagnostic" msgid="3121238373951637049">"Tillåter att ett program läser och skriver till en resurs som ägs av diag-gruppen; till exempel filer i /dev. Detta kan eventuellt påverka systemets stabilitet och säkerhet. Detta bör ENDAST används av tillverkaren eller operatören för maskinvaruspecifik diagnostik."</string> <string name="permlab_changeComponentState" msgid="79425198834329406">"aktivera eller inaktivera programkomponenter"</string> @@ -434,8 +436,7 @@ <string name="policydesc_wipeData" msgid="2314060933796396205">"Återställ fabriksinställningarna och ta bort alla data utan någon bekräftelse."</string> <!-- no translation found for policylab_setGlobalProxy (2784828293747791446) --> <skip /> - <!-- no translation found for policydesc_setGlobalProxy (6387497466660154931) --> - <skip /> + <string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"Ange vilken global proxyserver som ska användas när policyn är aktiverad. Endast den första enhetsadministratören anger den faktiska globala proxyservern."</string> <string-array name="phoneTypes"> <item msgid="8901098336658710359">"Hem"</item> <item msgid="869923650527136615">"Mobil"</item> @@ -529,6 +530,14 @@ <string name="orgTypeWork" msgid="29268870505363872">"Arbete"</string> <string name="orgTypeOther" msgid="3951781131570124082">"Övrigt"</string> <string name="orgTypeCustom" msgid="225523415372088322">"Anpassad"</string> + <!-- no translation found for sipAddressTypeCustom (2473580593111590945) --> + <skip /> + <!-- no translation found for sipAddressTypeHome (6093598181069359295) --> + <skip /> + <!-- no translation found for sipAddressTypeWork (6920725730797099047) --> + <skip /> + <!-- no translation found for sipAddressTypeOther (4408436162950119849) --> + <skip /> <string name="keyguard_password_enter_pin_code" msgid="3731488827218876115">"Ange PIN-kod"</string> <string name="keyguard_password_enter_password_code" msgid="9138158344813213754">"Ange lösenord för att låsa upp"</string> <string name="keyguard_password_enter_pin_password_code" msgid="638347075625491514">"Ange PIN-kod för att låsa upp"</string> @@ -589,6 +598,8 @@ <string name="js_dialog_before_unload" msgid="1901675448179653089">"Vill du lämna den här den här sidan?"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n"Tryck på OK om du vill fortsätta eller på Avbryt om du vill vara kvar på den aktuella sidan."</string> <string name="save_password_label" msgid="6860261758665825069">"Bekräfta"</string> <string name="double_tap_toast" msgid="1068216937244567247">"Tips! Dubbelklicka om du vill zooma in eller ut."</string> + <!-- no translation found for autofill_this_form (8940110866775097494) --> + <skip /> <string name="permlab_readHistoryBookmarks" msgid="1284843728203412135">"läsa webbläsarhistorik och bokmärken"</string> <string name="permdesc_readHistoryBookmarks" msgid="4981489815467617191">"Tillåter att program läser alla webbadresser som webbläsaren har öppnat och alla webbläsarens bokmärken."</string> <string name="permlab_writeHistoryBookmarks" msgid="9009434109836280374">"skriva webbläsarhistorik och bokmärken"</string> @@ -875,10 +886,10 @@ <string name="throttle_warning_notification_message" msgid="2609734763845705708">"Tryck om du vill veta mer om mobildataanvändning"</string> <string name="throttled_notification_title" msgid="6269541897729781332">"Gränsen för mobildata har överskridits"</string> <string name="throttled_notification_message" msgid="4712369856601275146">"Tryck om du vill veta mer om mobildataanvändning"</string> - <!-- no translation found for no_matches (8129421908915840737) --> - <skip /> - <!-- no translation found for find_on_page (1946799233822820384) --> - <skip /> - <!-- no translation found for matches_found:one (8167147081136579439) --> - <!-- no translation found for matches_found:other (4641872797067609177) --> + <string name="no_matches" msgid="8129421908915840737">"Inga träffar"</string> + <string name="find_on_page" msgid="1946799233822820384">"Sök på sidan"</string> + <plurals name="matches_found"> + <item quantity="one" msgid="8167147081136579439">"1 träff"</item> + <item quantity="other" msgid="4641872797067609177">"<xliff:g id="INDEX">%d</xliff:g> av <xliff:g id="TOTAL">%d</xliff:g>"</item> + </plurals> </resources> diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml index 78a1a3e..6ac5b86d 100644 --- a/core/res/res/values-tr/strings.xml +++ b/core/res/res/values-tr/strings.xml @@ -266,8 +266,10 @@ <string name="permdesc_clearAppCache" msgid="7740465694193671402">"Uygulamaların uygulama önbelleği dizinindeki dosyaları silerek telefonda yer açmasına izin verir. Erişim genellikle sistem işlemlerine ve yüksek düzeyde kısıtlı olarak verilir."</string> <string name="permlab_movePackage" msgid="728454979946503926">"Uygulama kaynaklarını taşı"</string> <string name="permdesc_movePackage" msgid="6323049291923925277">"Bir uygulamanın, uygulama kaynaklarını dahili ve harici ortamlar arasında taşımasına olanak tanır."</string> - <string name="permlab_readLogs" msgid="4811921703882532070">"sistem günlük dosyalarını oku"</string> - <string name="permdesc_readLogs" msgid="2257937955580475902">"Uygulamaların sistemin çeşitli günlük dosyalarından okumalarına izin verir. Bu, uygulamaların telefon ile neler yaptığınız ile ilgili genel bilgi bulmasına izin verir, ancak bunlar kişisel veya özel bir bilgi içermemelidir."</string> + <!-- no translation found for permlab_readLogs (6615778543198967614) --> + <skip /> + <!-- no translation found for permdesc_readLogs (8896449437464867766) --> + <skip /> <string name="permlab_diagnostic" msgid="8076743953908000342">"sahibi tanılama olan kaynakları oku/bunlara yaz"</string> <string name="permdesc_diagnostic" msgid="3121238373951637049">"Uygulamanın tanılama grubundaki bir kaynağa ait herhangi bir kaynağı; örneğin /dev içindeki dosyaları okumasına ve bunlara yazmasına izin verir. Bu işlevin sistem kararlılığını ve güvenliğini olumsuz etkileme olasılığı vardır. Üretici veya operatör tarafından YALNIZCA donanıma özgü tanılama için kullanılmalıdır."</string> <string name="permlab_changeComponentState" msgid="79425198834329406">"uygulama bileşenlerini etkinleştir veya devre dışı bırak"</string> @@ -434,8 +436,7 @@ <string name="policydesc_wipeData" msgid="2314060933796396205">"Tüm verilerinizi onay olmadan silmek için fabrika ayarlarına sıfırlayın."</string> <!-- no translation found for policylab_setGlobalProxy (2784828293747791446) --> <skip /> - <!-- no translation found for policydesc_setGlobalProxy (6387497466660154931) --> - <skip /> + <string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"Politika etkin olduğunda kullanılacak cihaz genelinde geçerli proxy\'yi ayarlayın. Etkin genel proxy\'yi yalnızca ilk cihaz yöneticisi ayarlar."</string> <string-array name="phoneTypes"> <item msgid="8901098336658710359">"Ev"</item> <item msgid="869923650527136615">"Mobil"</item> @@ -529,6 +530,14 @@ <string name="orgTypeWork" msgid="29268870505363872">"İş"</string> <string name="orgTypeOther" msgid="3951781131570124082">"Diğer"</string> <string name="orgTypeCustom" msgid="225523415372088322">"Özel"</string> + <!-- no translation found for sipAddressTypeCustom (2473580593111590945) --> + <skip /> + <!-- no translation found for sipAddressTypeHome (6093598181069359295) --> + <skip /> + <!-- no translation found for sipAddressTypeWork (6920725730797099047) --> + <skip /> + <!-- no translation found for sipAddressTypeOther (4408436162950119849) --> + <skip /> <string name="keyguard_password_enter_pin_code" msgid="3731488827218876115">"PIN kodunu gir"</string> <string name="keyguard_password_enter_password_code" msgid="9138158344813213754">"Kilidi açmak için şifreyi girin"</string> <string name="keyguard_password_enter_pin_password_code" msgid="638347075625491514">"Kilidi açmak için PIN\'i girin"</string> @@ -589,6 +598,8 @@ <string name="js_dialog_before_unload" msgid="1901675448179653089">"Bu sayfadan ayrılıyor musunuz?"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n"Devam etmek için Tamam\'ı, sayfada kalmak için İptal\'i tıklatın."</string> <string name="save_password_label" msgid="6860261758665825069">"Onayla"</string> <string name="double_tap_toast" msgid="1068216937244567247">"İpucu: Yakınlaştırmak ve uzaklaştırmak için iki kez hafifçe vurun."</string> + <!-- no translation found for autofill_this_form (8940110866775097494) --> + <skip /> <string name="permlab_readHistoryBookmarks" msgid="1284843728203412135">"Tarayıcı geçmişini ve favorileri oku"</string> <string name="permdesc_readHistoryBookmarks" msgid="4981489815467617191">"Uygulamaya Tarayıcının ziyaret etmiş olduğu tüm URL\'leri ve Tarayıcının tüm favorilerini okuma izni verir."</string> <string name="permlab_writeHistoryBookmarks" msgid="9009434109836280374">"Tarayıcı geçmişini ve favorileri yaz"</string> @@ -875,10 +886,10 @@ <string name="throttle_warning_notification_message" msgid="2609734763845705708">"Mobil veri kullanımı hakkında daha fazla bilgi edinmek için dokunun"</string> <string name="throttled_notification_title" msgid="6269541897729781332">"Mobil veri limiti aşıldı"</string> <string name="throttled_notification_message" msgid="4712369856601275146">"Mobil veri kullanımı hakkında daha fazla bilgi edinmek için dokunun"</string> - <!-- no translation found for no_matches (8129421908915840737) --> - <skip /> - <!-- no translation found for find_on_page (1946799233822820384) --> - <skip /> - <!-- no translation found for matches_found:one (8167147081136579439) --> - <!-- no translation found for matches_found:other (4641872797067609177) --> + <string name="no_matches" msgid="8129421908915840737">"Eşleşme yok"</string> + <string name="find_on_page" msgid="1946799233822820384">"Sayfada bul"</string> + <plurals name="matches_found"> + <item quantity="one" msgid="8167147081136579439">"1 eşleşme"</item> + <item quantity="other" msgid="4641872797067609177">"<xliff:g id="INDEX">%d</xliff:g> / <xliff:g id="TOTAL">%d</xliff:g>"</item> + </plurals> </resources> diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml index 9ad8c3e..697eb98 100644 --- a/core/res/res/values-zh-rCN/strings.xml +++ b/core/res/res/values-zh-rCN/strings.xml @@ -266,8 +266,10 @@ <string name="permdesc_clearAppCache" msgid="7740465694193671402">"允许应用程序通过删除应用程序缓存目录中的文件释放手机存储空间。通常此权限只适用于系统进程。"</string> <string name="permlab_movePackage" msgid="728454979946503926">"移动应用程序资源"</string> <string name="permdesc_movePackage" msgid="6323049291923925277">"允许应用程序在内部介质和外部介质之间移动应用程序资源。"</string> - <string name="permlab_readLogs" msgid="4811921703882532070">"读取系统日志文件"</string> - <string name="permdesc_readLogs" msgid="2257937955580475902">"允许应用程序从系统的各日志文件中读取信息。这样应用程序可以发现您的手机使用情况,但这些信息不应包含任何个人信息或保密信息。"</string> + <!-- no translation found for permlab_readLogs (6615778543198967614) --> + <skip /> + <!-- no translation found for permdesc_readLogs (8896449437464867766) --> + <skip /> <string name="permlab_diagnostic" msgid="8076743953908000342">"读取/写入诊断所拥有的资源"</string> <string name="permdesc_diagnostic" msgid="3121238373951637049">"允许应用程序读取/写入诊断组所拥有的任何资源(例如,/dev 中的文件)。这可能会影响系统稳定性和安全性。此权限仅供制造商或运营商诊断硬件问题。"</string> <string name="permlab_changeComponentState" msgid="79425198834329406">"启用或停用应用程序组件"</string> @@ -434,8 +436,7 @@ <string name="policydesc_wipeData" msgid="2314060933796396205">"恢复出厂设置,这会在不提示确认的情况下删除您的所有数据。"</string> <!-- no translation found for policylab_setGlobalProxy (2784828293747791446) --> <skip /> - <!-- no translation found for policydesc_setGlobalProxy (6387497466660154931) --> - <skip /> + <string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"请设置在启用政策的情况下要使用的设备全局代理。只有第一设备管理员才可设置有效的全局代理。"</string> <string-array name="phoneTypes"> <item msgid="8901098336658710359">"住宅"</item> <item msgid="869923650527136615">"手机"</item> @@ -529,6 +530,14 @@ <string name="orgTypeWork" msgid="29268870505363872">"公司"</string> <string name="orgTypeOther" msgid="3951781131570124082">"其他"</string> <string name="orgTypeCustom" msgid="225523415372088322">"自定义"</string> + <!-- no translation found for sipAddressTypeCustom (2473580593111590945) --> + <skip /> + <!-- no translation found for sipAddressTypeHome (6093598181069359295) --> + <skip /> + <!-- no translation found for sipAddressTypeWork (6920725730797099047) --> + <skip /> + <!-- no translation found for sipAddressTypeOther (4408436162950119849) --> + <skip /> <string name="keyguard_password_enter_pin_code" msgid="3731488827218876115">"输入 PIN 码"</string> <string name="keyguard_password_enter_password_code" msgid="9138158344813213754">"输入密码进行解锁"</string> <string name="keyguard_password_enter_pin_password_code" msgid="638347075625491514">"输入 PIN 进行解锁"</string> @@ -589,6 +598,8 @@ <string name="js_dialog_before_unload" msgid="1901675448179653089">"是否从该页面导航至它处?"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n"选择“确定”继续,或选择“取消”留在当前页面。"</string> <string name="save_password_label" msgid="6860261758665825069">"确认"</string> <string name="double_tap_toast" msgid="1068216937244567247">"提示:点按两次可放大和缩小。"</string> + <!-- no translation found for autofill_this_form (8940110866775097494) --> + <skip /> <string name="permlab_readHistoryBookmarks" msgid="1284843728203412135">"读取浏览器的历史记录和书签"</string> <string name="permdesc_readHistoryBookmarks" msgid="4981489815467617191">"允许应用程序读取用浏览器访问过的所有网址,以及浏览器的所有书签。"</string> <string name="permlab_writeHistoryBookmarks" msgid="9009434109836280374">"写入浏览器的历史记录和书签"</string> @@ -875,10 +886,10 @@ <string name="throttle_warning_notification_message" msgid="2609734763845705708">"轻触以了解有关手机流量详情"</string> <string name="throttled_notification_title" msgid="6269541897729781332">"已超出手机数据上限"</string> <string name="throttled_notification_message" msgid="4712369856601275146">"轻触以了解有关手机流量详情"</string> - <!-- no translation found for no_matches (8129421908915840737) --> - <skip /> - <!-- no translation found for find_on_page (1946799233822820384) --> - <skip /> - <!-- no translation found for matches_found:one (8167147081136579439) --> - <!-- no translation found for matches_found:other (4641872797067609177) --> + <string name="no_matches" msgid="8129421908915840737">"无匹配项"</string> + <string name="find_on_page" msgid="1946799233822820384">"在网页上查找"</string> + <plurals name="matches_found"> + <item quantity="one" msgid="8167147081136579439">"1 个匹配项"</item> + <item quantity="other" msgid="4641872797067609177">"第 <xliff:g id="INDEX">%d</xliff:g> 项,共 <xliff:g id="TOTAL">%d</xliff:g> 项"</item> + </plurals> </resources> diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml index ead49a7..5dbd8a3 100644 --- a/core/res/res/values-zh-rTW/strings.xml +++ b/core/res/res/values-zh-rTW/strings.xml @@ -266,8 +266,10 @@ <string name="permdesc_clearAppCache" msgid="7740465694193671402">"允許應用程式刪除快取目錄裡的檔案,釋放儲存空間。此操作通常受到系統程序嚴格限制。"</string> <string name="permlab_movePackage" msgid="728454979946503926">"移動應用程式資源"</string> <string name="permdesc_movePackage" msgid="6323049291923925277">"允許應用程式將應用程式資源從內部媒體移到外部媒體,反之亦可。"</string> - <string name="permlab_readLogs" msgid="4811921703882532070">"讀取系統記錄檔"</string> - <string name="permdesc_readLogs" msgid="2257937955580475902">"允許應用程式讀取系統記錄檔。此項操作可讓應用程式了解目前手機操作狀態,但內容應不含任何個人或隱私資訊。"</string> + <!-- no translation found for permlab_readLogs (6615778543198967614) --> + <skip /> + <!-- no translation found for permdesc_readLogs (8896449437464867766) --> + <skip /> <string name="permlab_diagnostic" msgid="8076743953908000342">"讀寫 diag 擁有的資源"</string> <string name="permdesc_diagnostic" msgid="3121238373951637049">"允許應用程式讀寫 diag 群組的資源;例如:/dev 裡的檔案。這可能會影響系統穩定性與安全性。此功能僅供製造商或技術人員用於硬體規格偵測。"</string> <string name="permlab_changeComponentState" msgid="79425198834329406">"啟用或停用應用程式元件"</string> @@ -434,8 +436,7 @@ <string name="policydesc_wipeData" msgid="2314060933796396205">"重設為原廠設定 (系統會刪除所有資料,且不會向您進行確認)。"</string> <!-- no translation found for policylab_setGlobalProxy (2784828293747791446) --> <skip /> - <!-- no translation found for policydesc_setGlobalProxy (6387497466660154931) --> - <skip /> + <string name="policydesc_setGlobalProxy" msgid="6387497466660154931">"設定政策啟用時所要使用的裝置全域 Proxy,只有第一個裝置管理員所設定的全域 Proxy 具有效力。"</string> <string-array name="phoneTypes"> <item msgid="8901098336658710359">"住家電話"</item> <item msgid="869923650527136615">"行動電話"</item> @@ -529,6 +530,14 @@ <string name="orgTypeWork" msgid="29268870505363872">"公司"</string> <string name="orgTypeOther" msgid="3951781131570124082">"其他"</string> <string name="orgTypeCustom" msgid="225523415372088322">"自訂"</string> + <!-- no translation found for sipAddressTypeCustom (2473580593111590945) --> + <skip /> + <!-- no translation found for sipAddressTypeHome (6093598181069359295) --> + <skip /> + <!-- no translation found for sipAddressTypeWork (6920725730797099047) --> + <skip /> + <!-- no translation found for sipAddressTypeOther (4408436162950119849) --> + <skip /> <string name="keyguard_password_enter_pin_code" msgid="3731488827218876115">"輸入 PIN 碼"</string> <string name="keyguard_password_enter_password_code" msgid="9138158344813213754">"輸入密碼即可解鎖"</string> <string name="keyguard_password_enter_pin_password_code" msgid="638347075625491514">"輸入 PIN 進行解鎖"</string> @@ -589,6 +598,8 @@ <string name="js_dialog_before_unload" msgid="1901675448179653089">"離開此頁?"\n\n"<xliff:g id="MESSAGE">%s</xliff:g>"\n\n" 選取 [確定] 離開此頁;或 [取消] 留在此頁。"</string> <string name="save_password_label" msgid="6860261758665825069">"確認"</string> <string name="double_tap_toast" msgid="1068216937244567247">"提示:輕按兩下可放大縮小。"</string> + <!-- no translation found for autofill_this_form (8940110866775097494) --> + <skip /> <string name="permlab_readHistoryBookmarks" msgid="1284843728203412135">"讀取瀏覽器的記錄與書籤"</string> <string name="permdesc_readHistoryBookmarks" msgid="4981489815467617191">"允許應用程式讀取瀏覽器曾經造訪過的所有網址,以及瀏覽器的所有書籤。"</string> <string name="permlab_writeHistoryBookmarks" msgid="9009434109836280374">"寫入瀏覽器的記錄與書籤"</string> @@ -875,10 +886,10 @@ <string name="throttle_warning_notification_message" msgid="2609734763845705708">"輕觸即可瞭解更多有關行動資料用量的詳細資訊"</string> <string name="throttled_notification_title" msgid="6269541897729781332">"已達行動資料上限"</string> <string name="throttled_notification_message" msgid="4712369856601275146">"輕觸即可瞭解更多有關行動資料用量的詳細資訊"</string> - <!-- no translation found for no_matches (8129421908915840737) --> - <skip /> - <!-- no translation found for find_on_page (1946799233822820384) --> - <skip /> - <!-- no translation found for matches_found:one (8167147081136579439) --> - <!-- no translation found for matches_found:other (4641872797067609177) --> + <string name="no_matches" msgid="8129421908915840737">"沒有相符項目"</string> + <string name="find_on_page" msgid="1946799233822820384">"在頁面中尋找"</string> + <plurals name="matches_found"> + <item quantity="one" msgid="8167147081136579439">"1 個相符項目"</item> + <item quantity="other" msgid="4641872797067609177">"第 <xliff:g id="INDEX">%d</xliff:g> 個相符項目 (共 <xliff:g id="TOTAL">%d</xliff:g> 個相符項目)"</item> + </plurals> </resources> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 2d4de8b..153dd29 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -1650,6 +1650,15 @@ <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> + <string name="permlab_setAlarm">set alarm in alarm clock</string> + <!-- Description of an application permission, listed so the user can choose whether + they want to allow the application to do this. --> + <string name="permdesc_setAlarm">Allows the application to set an alarm in + an installed alarm clock application. Some alarm clock applications may + not implement this feature.</string> + + <!-- Title of an application permission, listed so the user can choose whether + they want to allow the application to do this. --> <string name="permlab_writeGeolocationPermissions">Modify Browser geolocation permissions</string> <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> diff --git a/core/tests/coretests/src/android/content/pm/PackageManagerTests.java b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java index 975a4c2..1289a9e 100755 --- a/core/tests/coretests/src/android/content/pm/PackageManagerTests.java +++ b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java @@ -167,8 +167,7 @@ public class PackageManagerTests extends AndroidTestCase { return ipm; } - public boolean invokeInstallPackage(Uri packageURI, int flags, - GenericReceiver receiver) throws Exception { + public boolean invokeInstallPackage(Uri packageURI, int flags, GenericReceiver receiver) { PackageInstallObserver observer = new PackageInstallObserver(); final boolean received = false; mContext.registerReceiver(receiver, receiver.filter); @@ -180,11 +179,15 @@ public class PackageManagerTests extends AndroidTestCase { getPm().installPackage(packageURI, observer, flags, null); long waitTime = 0; while((!observer.isDone()) && (waitTime < MAX_WAIT_TIME) ) { - observer.wait(WAIT_TIME_INCR); - waitTime += WAIT_TIME_INCR; + try { + observer.wait(WAIT_TIME_INCR); + waitTime += WAIT_TIME_INCR; + } catch (InterruptedException e) { + Log.i(TAG, "Interrupted during sleep", e); + } } if(!observer.isDone()) { - throw new Exception("Timed out waiting for packageInstalled callback"); + fail("Timed out waiting for packageInstalled callback"); } if (observer.returnCode != PackageManager.INSTALL_SUCCEEDED) { Log.i(TAG, "Failed to install with error code = " + observer.returnCode); @@ -193,11 +196,15 @@ public class PackageManagerTests extends AndroidTestCase { // Verify we received the broadcast waitTime = 0; while((!receiver.isDone()) && (waitTime < MAX_WAIT_TIME) ) { - receiver.wait(WAIT_TIME_INCR); - waitTime += WAIT_TIME_INCR; + try { + receiver.wait(WAIT_TIME_INCR); + waitTime += WAIT_TIME_INCR; + } catch (InterruptedException e) { + Log.i(TAG, "Interrupted during sleep", e); + } } if(!receiver.isDone()) { - throw new Exception("Timed out waiting for PACKAGE_ADDED notification"); + fail("Timed out waiting for PACKAGE_ADDED notification"); } return receiver.received; } @@ -207,7 +214,7 @@ public class PackageManagerTests extends AndroidTestCase { } } - public void invokeInstallPackageFail(Uri packageURI, int flags, int result) throws Exception { + public void invokeInstallPackageFail(Uri packageURI, int flags, int result) { PackageInstallObserver observer = new PackageInstallObserver(); try { // Wait on observer @@ -215,11 +222,15 @@ public class PackageManagerTests extends AndroidTestCase { getPm().installPackage(packageURI, observer, flags, null); long waitTime = 0; while((!observer.isDone()) && (waitTime < MAX_WAIT_TIME) ) { - observer.wait(WAIT_TIME_INCR); - waitTime += WAIT_TIME_INCR; + try { + observer.wait(WAIT_TIME_INCR); + waitTime += WAIT_TIME_INCR; + } catch (InterruptedException e) { + Log.i(TAG, "Interrupted during sleep", e); + } } if(!observer.isDone()) { - throw new Exception("Timed out waiting for packageInstalled callback"); + fail("Timed out waiting for packageInstalled callback"); } assertEquals(observer.returnCode, result); } @@ -265,7 +276,7 @@ public class PackageManagerTests extends AndroidTestCase { Environment.getExternalStorageDirectory().getPath()); sdSize = (long)sdStats.getAvailableBlocks() * (long)sdStats.getBlockSize(); - // TODO check for thesholds here + // TODO check for thresholds here return pkgLen <= sdSize; } @@ -368,12 +379,21 @@ public class PackageManagerTests extends AndroidTestCase { assertFalse((info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0); assertTrue(info.nativeLibraryDir.startsWith(dataDir.getPath())); } else if (rLoc == INSTALL_LOC_SD){ - assertTrue((info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0); - assertTrue(srcPath.startsWith(SECURE_CONTAINERS_PREFIX)); - assertTrue(publicSrcPath.startsWith(SECURE_CONTAINERS_PREFIX)); - assertTrue(info.nativeLibraryDir.startsWith(SECURE_CONTAINERS_PREFIX)); + assertTrue("Application flags (" + info.flags + + ") should contain FLAG_EXTERNAL_STORAGE", + (info.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0); + assertTrue("The APK path (" + srcPath + ") should start with " + + SECURE_CONTAINERS_PREFIX, srcPath + .startsWith(SECURE_CONTAINERS_PREFIX)); + assertTrue("The public APK path (" + publicSrcPath + ") should start with " + + SECURE_CONTAINERS_PREFIX, publicSrcPath + .startsWith(SECURE_CONTAINERS_PREFIX)); + assertTrue("The native library path (" + info.nativeLibraryDir + + ") should start with " + SECURE_CONTAINERS_PREFIX, + info.nativeLibraryDir.startsWith(SECURE_CONTAINERS_PREFIX)); } else { // TODO handle error. Install should have failed. + fail("Install should have failed"); } } } catch (NameNotFoundException e) { @@ -544,8 +564,6 @@ public class PackageManagerTests extends AndroidTestCase { // Verify installed information assertInstall(pkg, flags, expInstallLocation); } - } catch (Exception e) { - failStr("Failed with exception : " + e); } finally { if (cleanUp) { cleanUpInstall(ip); diff --git a/data/fonts/Ahem.ttf b/data/fonts/Ahem.ttf Binary files differdeleted file mode 100644 index 17e6c60..0000000 --- a/data/fonts/Ahem.ttf +++ /dev/null diff --git a/data/fonts/fonts.xml b/data/fonts/fonts.xml index 6bcaaaf..1fd7bba 100644 --- a/data/fonts/fonts.xml +++ b/data/fonts/fonts.xml @@ -44,5 +44,5 @@ <name>monaco</name> </font> <fallback ttf="DroidSansFallback" /> - <fallback ttf="DroidSansJapanese" /> -</fonts>
\ No newline at end of file + <fallback ttf="MTLmr3m" /> +</fonts> diff --git a/docs/html/guide/tutorials/notepad/notepad-ex2.jd b/docs/html/guide/tutorials/notepad/notepad-ex2.jd index a945a62..854731f 100644 --- a/docs/html/guide/tutorials/notepad/notepad-ex2.jd +++ b/docs/html/guide/tutorials/notepad/notepad-ex2.jd @@ -87,8 +87,8 @@ Open the Notepadv2 class.</p> menu callback used for the options menu. Here, we add just one line, which will add a menu item to delete a note. Call <code>menu.add()</code> like so: <pre> -public boolean onCreateContextMenu(Menu menu, View v - ContextMenuInfo menuInfo) { +public void onCreateContextMenu(Menu menu, View v, + ContextMenu.ContextMenuInfo menuInfo) { super.onCreateContextMenu(menu, v, menuInfo); menu.add(0, DELETE_ID, 0, R.string.menu_delete); }</pre> diff --git a/docs/html/resources/tutorials/notepad/notepad-ex2.jd b/docs/html/resources/tutorials/notepad/notepad-ex2.jd index 289b5fe..499b796 100644 --- a/docs/html/resources/tutorials/notepad/notepad-ex2.jd +++ b/docs/html/resources/tutorials/notepad/notepad-ex2.jd @@ -87,8 +87,8 @@ Open the Notepadv2 class.</p> menu callback used for the options menu. Here, we add just one line, which will add a menu item to delete a note. Call <code>menu.add()</code> like so: <pre> -public boolean onCreateContextMenu(Menu menu, View v - ContextMenuInfo menuInfo) { +public void onCreateContextMenu(Menu menu, View v, + ContextMenu.ContextMenuInfo menuInfo) { super.onCreateContextMenu(menu, v, menuInfo); menu.add(0, DELETE_ID, 0, R.string.menu_delete); }</pre> diff --git a/docs/html/sdk/adt_download.jd b/docs/html/sdk/adt_download.jd index 5e642d7..3da576a 100644 --- a/docs/html/sdk/adt_download.jd +++ b/docs/html/sdk/adt_download.jd @@ -22,10 +22,17 @@ ADT Installation</a>.</p> <th>Notes</th> </tr> <tr> + <td>0.9.9</td> + <td><a href="http://dl-ssl.google.com/android/ADT-0.9.9.zip">ADT-0.9.9.zip</a></td> + <td><nobr>8301681 bytes</nobr></td> + <td>7deff0c9b25940a74cea7a0815a3bc36</td> + <td>Requires SDK Tools, Revision 7 <em><nobr>September 2010</nobr></em></td> + </tr> + <tr> <td>0.9.8</td> <td><a href="http://dl-ssl.google.com/android/ADT-0.9.8.zip">ADT-0.9.8.zip</a></td> - <td><nobr>8703591 bytes</nobr></td> - <td>22070f8e52924605a3b3abf87c1ba39f</td> + <td><nobr>8301417 bytes</nobr></td> + <td>27e0de800512f13feae46fb554e6ee2f</td> <td>Requires SDK Tools, Revision 7 <em><nobr>September 2010</nobr></em></td> </tr> <tr> diff --git a/docs/html/sdk/eclipse-adt.jd b/docs/html/sdk/eclipse-adt.jd index a984d56..9f3c8b0 100644 --- a/docs/html/sdk/eclipse-adt.jd +++ b/docs/html/sdk/eclipse-adt.jd @@ -1,9 +1,9 @@ page.title=ADT Plugin for Eclipse sdk.preview=0 -adt.zip.version=0.9.8 -adt.zip.download=ADT-0.9.8.zip -adt.zip.bytes=8703591 -adt.zip.checksum=22070f8e52924605a3b3abf87c1ba39f +adt.zip.version=0.9.9 +adt.zip.download=ADT-0.9.9.zip +adt.zip.bytes=8301681 +adt.zip.checksum=7deff0c9b25940a74cea7a0815a3bc36 @jd:body @@ -100,11 +100,40 @@ padding: .25em 1em; </style> - - <div class="toggleable opened"> <a href="#" onclick="return toggleDiv(this)"> <img src="{@docRoot}assets/images/triangle-opened.png" class="toggle-img" height="9px" width="9px" /> +ADT 0.9.9</a> <em>(September 2010)</em> + <div class="toggleme"> + + +</ul> +</dd> + +<dl> + +<dt>Dependencies:</dt> + +<dd><p>ADT 0.9.9 replaces ADT 0.9.8 and is designed for use with SDK Tools r7 +and later. ADT 0.9.9 includes the ADT 0.9.8 features as well as an important +bugfix, so we recommend that you upgrade as soon as possible. If you haven't +already installed SDK Tools r7 into your SDK, use the Android SDK Manager to do +so.</p></dd> + +<dt>General notes:</dt> +<dd> +<ul> +<li>Fixes a problem in project import, in which source files were deleted in some cases.</li> +<li>Includes all other ADT 0.9.8 features (see below).</li> +</ul> +</dd> +</dl> + </div> +</div> + +<div class="toggleable closed"> + <a href="#" onclick="return toggleDiv(this)"> + <img src="{@docRoot}assets/images/triangle-closed.png" class="toggle-img" height="9px" width="9px" /> ADT 0.9.8</a> <em>(September 2010)</em> <div class="toggleme"> @@ -116,9 +145,7 @@ ADT 0.9.8</a> <em>(September 2010)</em> <dt>Dependencies:</dt> -<dd><p>ADT 0.9.8 is designed for use with SDK Tools r7 and later. Before -updating to ADT 0.9.8, we highly recommend that you use the Android SDK and -AVD Manager to install SDK Tools r7 into your SDK.</p></dd> +<dd><p>ADT 0.9.8 is now deprecated. Please use ADT 0.9.9 instead.</p></dd> <dt>General notes:</dt> <dd> diff --git a/docs/html/sdk/requirements.jd b/docs/html/sdk/requirements.jd index d710b8e..7b11654 100644 --- a/docs/html/sdk/requirements.jd +++ b/docs/html/sdk/requirements.jd @@ -6,7 +6,7 @@ Android applications using the Android SDK. </p> <h3>Supported Operating Systems</h3> <ul> - <li>Windows XP (32-bit) or Vista (32- or 64-bit)</li> + <li>Windows XP (32-bit), Vista (32- or 64-bit), or Windows 7 (32- or 64-bit)</li> <li>Mac OS X 10.5.8 or later (x86 only)</li> <li>Linux (tested on Linux Ubuntu Hardy Heron) <ul> diff --git a/docs/html/sdk/sdk_toc.cs b/docs/html/sdk/sdk_toc.cs index a665e95..fdf4438 100644 --- a/docs/html/sdk/sdk_toc.cs +++ b/docs/html/sdk/sdk_toc.cs @@ -94,7 +94,7 @@ <span style="display:none" class="zh-TW"></span> </h2> <ul> - <li><a href="<?cs var:toroot ?>sdk/eclipse-adt.html">ADT 0.9.8 + <li><a href="<?cs var:toroot ?>sdk/eclipse-adt.html">ADT 0.9.9 <span style="display:none" class="de"></span> <span style="display:none" class="es"></span> <span style="display:none" class="fr"></span> diff --git a/graphics/java/android/renderscript/Program.java b/graphics/java/android/renderscript/Program.java index c6ed72a..ffcdbbc 100644 --- a/graphics/java/android/renderscript/Program.java +++ b/graphics/java/android/renderscript/Program.java @@ -154,6 +154,13 @@ public class Program extends BaseObj { mOutputs[mOutputCount++] = e; } + void resetConstant() { + mConstantCount = 0; + for(int i = 0; i < MAX_CONSTANT; i ++) { + mConstants[i] = null; + } + } + public int addConstant(Type t) throws IllegalStateException { // Should check for consistant and non-conflicting names... if(mConstantCount >= MAX_CONSTANT) { @@ -165,7 +172,7 @@ public class Program extends BaseObj { public BaseProgramBuilder setTextureCount(int count) throws IllegalArgumentException { // Should check for consistant and non-conflicting names... - if(count >= MAX_CONSTANT) { + if(count >= MAX_TEXTURE) { throw new IllegalArgumentException("Max texture count exceeded."); } mTextureCount = count; diff --git a/graphics/java/android/renderscript/ProgramFragment.java b/graphics/java/android/renderscript/ProgramFragment.java index 00c5cf1..c1d6428 100644 --- a/graphics/java/android/renderscript/ProgramFragment.java +++ b/graphics/java/android/renderscript/ProgramFragment.java @@ -55,7 +55,7 @@ public class ProgramFragment extends Program { tmp[idx++] = 3; tmp[idx++] = mTextureCount; - int id = mRS.nProgramFragmentCreate2(mShader, tmp); + int id = mRS.nProgramFragmentCreate(mShader, tmp); ProgramFragment pf = new ProgramFragment(id, mRS); initProgram(pf); return pf; @@ -199,6 +199,7 @@ public class ProgramFragment extends Program { mNumTextures ++; } } + resetConstant(); buildShaderString(); Type constType = null; if (!mVaryingColorEnable) { diff --git a/graphics/java/android/renderscript/ProgramVertex.java b/graphics/java/android/renderscript/ProgramVertex.java index 119db69..63e2598 100644 --- a/graphics/java/android/renderscript/ProgramVertex.java +++ b/graphics/java/android/renderscript/ProgramVertex.java @@ -64,7 +64,7 @@ public class ProgramVertex extends Program { tmp[idx++] = 3; tmp[idx++] = mTextureCount; - int id = mRS.nProgramVertexCreate2(mShader, tmp); + int id = mRS.nProgramVertexCreate(mShader, tmp); ProgramVertex pv = new ProgramVertex(id, mRS); initProgram(pv); return pv; diff --git a/graphics/java/android/renderscript/RenderScript.java b/graphics/java/android/renderscript/RenderScript.java index 159e070..2aa3e84 100644 --- a/graphics/java/android/renderscript/RenderScript.java +++ b/graphics/java/android/renderscript/RenderScript.java @@ -447,25 +447,14 @@ public class RenderScript { synchronized void nProgramBindSampler(int vpf, int slot, int s) { rsnProgramBindSampler(mContext, vpf, slot, s); } - - native int rsnProgramFragmentCreate(int con, int[] params); - synchronized int nProgramFragmentCreate(int[] params) { - return rsnProgramFragmentCreate(mContext, params); - } - native int rsnProgramFragmentCreate2(int con, String shader, int[] params); - synchronized int nProgramFragmentCreate2(String shader, int[] params) { - return rsnProgramFragmentCreate2(mContext, shader, params); + native int rsnProgramFragmentCreate(int con, String shader, int[] params); + synchronized int nProgramFragmentCreate(String shader, int[] params) { + return rsnProgramFragmentCreate(mContext, shader, params); } - - native int rsnProgramVertexCreate(int con, boolean texMat); - synchronized int nProgramVertexCreate(boolean texMat) { - return rsnProgramVertexCreate(mContext, texMat); + native int rsnProgramVertexCreate(int con, String shader, int[] params); + synchronized int nProgramVertexCreate(String shader, int[] params) { + return rsnProgramVertexCreate(mContext, shader, params); } - native int rsnProgramVertexCreate2(int con, String shader, int[] params); - synchronized int nProgramVertexCreate2(String shader, int[] params) { - return rsnProgramVertexCreate2(mContext, shader, params); - } - native int rsnMeshCreate(int con, int vtxCount, int indexCount); synchronized int nMeshCreate(int vtxCount, int indexCount) { diff --git a/graphics/jni/android_renderscript_RenderScript.cpp b/graphics/jni/android_renderscript_RenderScript.cpp index ee2080e..6aed11b 100644 --- a/graphics/jni/android_renderscript_RenderScript.cpp +++ b/graphics/jni/android_renderscript_RenderScript.cpp @@ -983,29 +983,16 @@ nProgramBindSampler(JNIEnv *_env, jobject _this, RsContext con, jint vpf, jint s // --------------------------------------------------------------------------- static jint -nProgramFragmentCreate(JNIEnv *_env, jobject _this, RsContext con, jintArray params) -{ - jint *paramPtr = _env->GetIntArrayElements(params, NULL); - jint paramLen = _env->GetArrayLength(params); - - LOG_API("nProgramFragmentCreate, con(%p), paramLen(%i)", con, paramLen); - - jint ret = (jint)rsProgramFragmentCreate(con, (uint32_t *)paramPtr, paramLen); - _env->ReleaseIntArrayElements(params, paramPtr, JNI_ABORT); - return ret; -} - -static jint -nProgramFragmentCreate2(JNIEnv *_env, jobject _this, RsContext con, jstring shader, jintArray params) +nProgramFragmentCreate(JNIEnv *_env, jobject _this, RsContext con, jstring shader, jintArray params) { const char* shaderUTF = _env->GetStringUTFChars(shader, NULL); jint shaderLen = _env->GetStringUTFLength(shader); jint *paramPtr = _env->GetIntArrayElements(params, NULL); jint paramLen = _env->GetArrayLength(params); - LOG_API("nProgramFragmentCreate2, con(%p), shaderLen(%i), paramLen(%i)", con, shaderLen, paramLen); + LOG_API("nProgramFragmentCreate, con(%p), shaderLen(%i), paramLen(%i)", con, shaderLen, paramLen); - jint ret = (jint)rsProgramFragmentCreate2(con, shaderUTF, shaderLen, (uint32_t *)paramPtr, paramLen); + jint ret = (jint)rsProgramFragmentCreate(con, shaderUTF, shaderLen, (uint32_t *)paramPtr, paramLen); _env->ReleaseStringUTFChars(shader, shaderUTF); _env->ReleaseIntArrayElements(params, paramPtr, JNI_ABORT); return ret; @@ -1015,23 +1002,16 @@ nProgramFragmentCreate2(JNIEnv *_env, jobject _this, RsContext con, jstring shad // --------------------------------------------------------------------------- static jint -nProgramVertexCreate(JNIEnv *_env, jobject _this, RsContext con, jboolean texMat) -{ - LOG_API("nProgramVertexCreate, con(%p), texMat(%i)", con, texMat); - return (jint)rsProgramVertexCreate(con, texMat); -} - -static jint -nProgramVertexCreate2(JNIEnv *_env, jobject _this, RsContext con, jstring shader, jintArray params) +nProgramVertexCreate(JNIEnv *_env, jobject _this, RsContext con, jstring shader, jintArray params) { const char* shaderUTF = _env->GetStringUTFChars(shader, NULL); jint shaderLen = _env->GetStringUTFLength(shader); jint *paramPtr = _env->GetIntArrayElements(params, NULL); jint paramLen = _env->GetArrayLength(params); - LOG_API("nProgramVertexCreate2, con(%p), shaderLen(%i), paramLen(%i)", con, shaderLen, paramLen); + LOG_API("nProgramVertexCreate, con(%p), shaderLen(%i), paramLen(%i)", con, shaderLen, paramLen); - jint ret = (jint)rsProgramVertexCreate2(con, shaderUTF, shaderLen, (uint32_t *)paramPtr, paramLen); + jint ret = (jint)rsProgramVertexCreate(con, shaderUTF, shaderLen, (uint32_t *)paramPtr, paramLen); _env->ReleaseStringUTFChars(shader, shaderUTF); _env->ReleaseIntArrayElements(params, paramPtr, JNI_ABORT); return ret; @@ -1217,36 +1197,36 @@ static JNINativeMethod methods[] = { // All methods below are thread protected in java. -{"rsnContextCreate", "(II)I", (void*)nContextCreate }, -{"rsnContextCreateGL", "(IIZ)I", (void*)nContextCreateGL }, +{"rsnContextCreate", "(II)I", (void*)nContextCreate }, +{"rsnContextCreateGL", "(IIZ)I", (void*)nContextCreateGL }, {"rsnContextFinish", "(I)V", (void*)nContextFinish }, {"rsnContextSetPriority", "(II)V", (void*)nContextSetPriority }, {"rsnContextSetSurface", "(IIILandroid/view/Surface;)V", (void*)nContextSetSurface }, -{"rsnContextDestroy", "(I)V", (void*)nContextDestroy }, +{"rsnContextDestroy", "(I)V", (void*)nContextDestroy }, {"rsnContextDump", "(II)V", (void*)nContextDump }, {"rsnContextPause", "(I)V", (void*)nContextPause }, {"rsnContextResume", "(I)V", (void*)nContextResume }, {"rsnAssignName", "(II[B)V", (void*)nAssignName }, -{"rsnGetName", "(II)Ljava/lang/String;", (void*)nGetName }, +{"rsnGetName", "(II)Ljava/lang/String;", (void*)nGetName }, {"rsnObjDestroy", "(II)V", (void*)nObjDestroy }, {"rsnFileOpen", "(I[B)I", (void*)nFileOpen }, {"rsnFileA3DCreateFromAssetStream", "(II)I", (void*)nFileA3DCreateFromAssetStream }, {"rsnFileA3DGetNumIndexEntries", "(II)I", (void*)nFileA3DGetNumIndexEntries }, -{"rsnFileA3DGetIndexEntries", "(III[I[Ljava/lang/String;)V", (void*)nFileA3DGetIndexEntries }, +{"rsnFileA3DGetIndexEntries", "(III[I[Ljava/lang/String;)V", (void*)nFileA3DGetIndexEntries }, {"rsnFileA3DGetEntryByIndex", "(III)I", (void*)nFileA3DGetEntryByIndex }, -{"rsnFontCreateFromFile", "(ILjava/lang/String;II)I", (void*)nFontCreateFromFile }, +{"rsnFontCreateFromFile", "(ILjava/lang/String;II)I", (void*)nFontCreateFromFile }, {"rsnElementCreate", "(IIIZI)I", (void*)nElementCreate }, {"rsnElementCreate2", "(I[I[Ljava/lang/String;[I)I", (void*)nElementCreate2 }, {"rsnElementGetNativeData", "(II[I)V", (void*)nElementGetNativeData }, -{"rsnElementGetSubElements", "(II[I[Ljava/lang/String;)V", (void*)nElementGetSubElements }, +{"rsnElementGetSubElements", "(II[I[Ljava/lang/String;)V", (void*)nElementGetSubElements }, {"rsnTypeBegin", "(II)V", (void*)nTypeBegin }, {"rsnTypeAdd", "(III)V", (void*)nTypeAdd }, {"rsnTypeCreate", "(I)I", (void*)nTypeCreate }, -{"rsnTypeGetNativeData", "(II[I)V", (void*)nTypeGetNativeData }, +{"rsnTypeGetNativeData", "(II[I)V", (void*)nTypeGetNativeData }, {"rsnAllocationCreateTyped", "(II)I", (void*)nAllocationCreateTyped }, {"rsnAllocationCreateFromBitmap", "(IIZLandroid/graphics/Bitmap;)I", (void*)nAllocationCreateFromBitmap }, @@ -1307,18 +1287,16 @@ static JNINativeMethod methods[] = { {"rsnProgramBindTexture", "(IIII)V", (void*)nProgramBindTexture }, {"rsnProgramBindSampler", "(IIII)V", (void*)nProgramBindSampler }, -{"rsnProgramFragmentCreate", "(I[I)I", (void*)nProgramFragmentCreate }, -{"rsnProgramFragmentCreate2", "(ILjava/lang/String;[I)I", (void*)nProgramFragmentCreate2 }, +{"rsnProgramFragmentCreate", "(ILjava/lang/String;[I)I", (void*)nProgramFragmentCreate }, -{"rsnProgramRasterCreate", "(IZZZ)I", (void*)nProgramRasterCreate }, +{"rsnProgramRasterCreate", "(IZZZ)I", (void*)nProgramRasterCreate }, {"rsnProgramRasterSetLineWidth", "(IIF)V", (void*)nProgramRasterSetLineWidth }, {"rsnProgramRasterSetCullMode", "(III)V", (void*)nProgramRasterSetCullMode }, -{"rsnProgramVertexCreate", "(IZ)I", (void*)nProgramVertexCreate }, -{"rsnProgramVertexCreate2", "(ILjava/lang/String;[I)I", (void*)nProgramVertexCreate2 }, +{"rsnProgramVertexCreate", "(ILjava/lang/String;[I)I", (void*)nProgramVertexCreate }, {"rsnContextBindRootScript", "(II)V", (void*)nContextBindRootScript }, -{"rsnContextBindProgramStore", "(II)V", (void*)nContextBindProgramStore }, +{"rsnContextBindProgramStore", "(II)V", (void*)nContextBindProgramStore }, {"rsnContextBindProgramFragment", "(II)V", (void*)nContextBindProgramFragment }, {"rsnContextBindProgramVertex", "(II)V", (void*)nContextBindProgramVertex }, {"rsnContextBindProgramRaster", "(II)V", (void*)nContextBindProgramRaster }, @@ -1333,7 +1311,7 @@ static JNINativeMethod methods[] = { {"rsnMeshGetVertexBufferCount", "(II)I", (void*)nMeshGetVertexBufferCount }, {"rsnMeshGetIndexCount", "(II)I", (void*)nMeshGetIndexCount }, -{"rsnMeshGetVertices", "(II[II)V", (void*)nMeshGetVertices }, +{"rsnMeshGetVertices", "(II[II)V", (void*)nMeshGetVertices }, {"rsnMeshGetIndices", "(II[I[II)V", (void*)nMeshGetIndices }, }; diff --git a/include/utils/ZipFileRO.h b/include/utils/ZipFileRO.h index 97d31f4..9668bde 100644 --- a/include/utils/ZipFileRO.h +++ b/include/utils/ZipFileRO.h @@ -24,8 +24,9 @@ #ifndef __LIBS_ZIPFILERO_H #define __LIBS_ZIPFILERO_H -#include "Errors.h" -#include "FileMap.h" +#include <utils/Errors.h> +#include <utils/FileMap.h> +#include <utils/threads.h> #include <stdio.h> #include <stdlib.h> @@ -211,6 +212,9 @@ private: /* open Zip archive */ int mFd; + /* Lock for handling the file descriptor (seeks, etc) */ + mutable Mutex mFdLock; + /* zip file name */ char* mFileName; diff --git a/libs/binder/IPCThreadState.cpp b/libs/binder/IPCThreadState.cpp index a3e117f..13c58f0 100644 --- a/libs/binder/IPCThreadState.cpp +++ b/libs/binder/IPCThreadState.cpp @@ -517,12 +517,26 @@ status_t IPCThreadState::transact(int32_t handle, } if ((flags & TF_ONE_WAY) == 0) { + #if 0 + if (code == 4) { // relayout + LOGI(">>>>>> CALLING transaction 4"); + } else { + LOGI(">>>>>> CALLING transaction %d", code); + } + #endif if (reply) { err = waitForResponse(reply); } else { Parcel fakeReply; err = waitForResponse(&fakeReply); } + #if 0 + if (code == 4) { // relayout + LOGI("<<<<<< RETURNING transaction 4"); + } else { + LOGI("<<<<<< RETURNING transaction %d", code); + } + #endif IF_LOG_TRANSACTIONS() { TextOutput::Bundle _b(alog); diff --git a/libs/rs/java/Samples/res/raw/multitexf.glsl b/libs/rs/java/Samples/res/raw/multitexf.glsl new file mode 100644 index 0000000..91151ad --- /dev/null +++ b/libs/rs/java/Samples/res/raw/multitexf.glsl @@ -0,0 +1,12 @@ +varying vec4 varTex0; + +void main() { + vec2 t0 = varTex0.xy; + lowp vec4 col0 = texture2D(UNI_Tex0, t0).rgba; + lowp vec4 col1 = texture2D(UNI_Tex1, t0*4.0).rgba; + lowp vec4 col2 = texture2D(UNI_Tex2, t0).rgba; + col0.xyz = col0.xyz*col1.xyz*1.5; + col0.xyz = mix(col0.xyz, col2.xyz, col2.w); + gl_FragColor = col0; +} + diff --git a/libs/rs/java/Samples/src/com/android/samples/RsList.java b/libs/rs/java/Samples/src/com/android/samples/RsList.java index d8c733d..0f6b1ac 100644 --- a/libs/rs/java/Samples/src/com/android/samples/RsList.java +++ b/libs/rs/java/Samples/src/com/android/samples/RsList.java @@ -54,7 +54,7 @@ public class RsList extends Activity { @Override protected void onResume() { // Ideally a game should implement onResume() and onPause() - // to take appropriate action when the activity looses focus + // to take appropriate action when the activity loses focus super.onResume(); mView.onResume(); } @@ -62,7 +62,7 @@ public class RsList extends Activity { @Override protected void onPause() { // Ideally a game should implement onResume() and onPause() - // to take appropriate action when the activity looses focus + // to take appropriate action when the activity loses focus super.onPause(); mView.onPause(); } diff --git a/libs/rs/java/Samples/src/com/android/samples/RsRenderStatesRS.java b/libs/rs/java/Samples/src/com/android/samples/RsRenderStatesRS.java index 18b94d9..d4e83d3 100644 --- a/libs/rs/java/Samples/src/com/android/samples/RsRenderStatesRS.java +++ b/libs/rs/java/Samples/src/com/android/samples/RsRenderStatesRS.java @@ -42,7 +42,7 @@ public class RsRenderStatesRS { mOptionsARGB.inScaled = false; mOptionsARGB.inPreferredConfig = Bitmap.Config.ARGB_8888; mMode = 0; - mMaxModes = 7; + mMaxModes = 8; initRS(); } @@ -68,6 +68,7 @@ public class RsRenderStatesRS { // Custom shaders private ProgramVertex mProgVertexCustom; private ProgramFragment mProgFragmentCustom; + private ProgramFragment mProgFragmentMultitex; private ScriptField_VertexShaderConstants_s mVSConst; private ScriptField_FragentShaderConstants_s mFSConst; @@ -214,8 +215,14 @@ public class RsRenderStatesRS { // Bind the source of constant data mProgFragmentCustom.bindConstants(mFSConst.getAllocation(), 0); + pfbCustom = new ProgramFragment.ShaderBuilder(mRS); + pfbCustom.setShader(mRes, R.raw.multitexf); + pfbCustom.setTextureCount(3); + mProgFragmentMultitex = pfbCustom.create(); + mScript.set_gProgVertexCustom(mProgVertexCustom); mScript.set_gProgFragmentCustom(mProgFragmentCustom); + mScript.set_gProgFragmentMultitex(mProgFragmentMultitex); } private Allocation loadTextureRGB(int id) { diff --git a/libs/rs/java/Samples/src/com/android/samples/rsrenderstates.rs b/libs/rs/java/Samples/src/com/android/samples/rsrenderstates.rs index c7bea93..659e1e4 100644 --- a/libs/rs/java/Samples/src/com/android/samples/rsrenderstates.rs +++ b/libs/rs/java/Samples/src/com/android/samples/rsrenderstates.rs @@ -60,6 +60,7 @@ VertexShaderInputs *gVSInputs; // Custom shaders we use for lighting rs_program_vertex gProgVertexCustom; rs_program_fragment gProgFragmentCustom; +rs_program_fragment gProgFragmentMultitex; #pragma rs export_var(gProgVertex, gProgFragmentColor, gProgFragmentTexture) #pragma rs export_var(gProgStoreBlendNoneDepth, gProgStoreBlendNone, gProgStoreBlendAlpha, gProgStoreBlendAdd) @@ -68,7 +69,7 @@ rs_program_fragment gProgFragmentCustom; #pragma rs export_var(gFontSans, gFontSerif, gFontSerifBold, gFontSerifItalic, gFontSerifBoldItalic, gFontMono) #pragma rs export_var(gLinearClamp, gLinearWrap, gMipLinearWrap, gNearestClamp) #pragma rs export_var(gCullBack, gCullFront) -#pragma rs export_var(gVSConstants, gFSConstants, gVSInputs, gProgVertexCustom, gProgFragmentCustom) +#pragma rs export_var(gVSConstants, gFSConstants, gVSInputs, gProgVertexCustom, gProgFragmentCustom, gProgFragmentMultitex) //What we are showing #pragma rs export_var(gDisplayMode) @@ -414,9 +415,38 @@ void displayCustomShaderSamples() { rsgFontColor(1.0f, 1.0f, 1.0f, 1.0f); rsgBindFont(gFontMono); - //rsgDrawText("Custom shader sample", 10, rsgGetHeight() - 10); + rsgDrawText("Custom shader sample", 10, rsgGetHeight() - 10); } +void displayMultitextureSample() { + bindProgramVertexOrtho(); + rs_matrix4x4 matrix; + rsMatrixLoadIdentity(&matrix); + rsgProgramVertexLoadModelMatrix(&matrix); + + // Fragment shader with texture + rsgBindProgramStore(gProgStoreBlendNone); + rsgBindProgramFragment(gProgFragmentMultitex); + rsgBindSampler(gProgFragmentMultitex, 0, gLinearClamp); + rsgBindSampler(gProgFragmentMultitex, 1, gLinearWrap); + rsgBindSampler(gProgFragmentMultitex, 2, gLinearClamp); + rsgBindTexture(gProgFragmentMultitex, 0, gTexOpaque); + rsgBindTexture(gProgFragmentMultitex, 1, gTexTorus); + rsgBindTexture(gProgFragmentMultitex, 2, gTexTransparent); + + float startX = 0, startY = 0; + float width = 256, height = 256; + rsgDrawQuadTexCoords(startX, startY, 0, 0, 0, + startX, startY + height, 0, 0, 1, + startX + width, startY + height, 0, 1, 1, + startX + width, startY, 0, 1, 0); + + rsgFontColor(1.0f, 1.0f, 1.0f, 1.0f); + rsgBindFont(gFontMono); + rsgDrawText("Custom shader with multitexturing", 10, 280); +} + + int root(int launchID) { gDt = rsGetDt(); @@ -446,6 +476,9 @@ int root(int launchID) { case 6: displayCustomShaderSamples(); break; + case 7: + displayMultitextureSample(); + break; } return 10; diff --git a/libs/rs/java/tests/src/com/android/rs/test/RSTestCore.java b/libs/rs/java/tests/src/com/android/rs/test/RSTestCore.java index cf9c6be..cd8f814 100644 --- a/libs/rs/java/tests/src/com/android/rs/test/RSTestCore.java +++ b/libs/rs/java/tests/src/com/android/rs/test/RSTestCore.java @@ -19,10 +19,13 @@ package com.android.rs.test; import android.content.res.Resources; import android.renderscript.*; import android.util.Log; +import java.util.ArrayList; +import java.util.ListIterator; public class RSTestCore { - public static final int PART_COUNT = 50000; + int mWidth; + int mHeight; public RSTestCore() { } @@ -30,31 +33,97 @@ public class RSTestCore { private Resources mRes; private RenderScriptGL mRS; - private ScriptC_test_root mRootScript; + private Font mFont; + ScriptField_ListAllocs_s mListAllocs; + int mLastX; + int mLastY; + private ScriptC_rslist mScript; - private boolean fp_mad() { - ScriptC_fp_mad s = new ScriptC_fp_mad(mRS, mRes, R.raw.fp_mad, true); - s.invoke_doTest(0, 0); - return true; - } - - private boolean rs_primitives_test() { - ScriptC_primitives s = new ScriptC_primitives(mRS, mRes, R.raw.primitives, true); - s.invoke_rs_primitives_test(0, 0); - return true; - } + private ArrayList<UnitTest> unitTests; public void init(RenderScriptGL rs, Resources res, int width, int height) { mRS = rs; mRes = res; + mWidth = width; + mHeight = height; + + mScript = new ScriptC_rslist(mRS, mRes, R.raw.rslist, true); + + unitTests = new ArrayList<UnitTest>(); + + unitTests.add(new UT_primitives(this, mRes)); + unitTests.add(new UT_fp_mad(this, mRes)); + /* + unitTests.add(new UnitTest("<Pass>", 1)); + unitTests.add(new UnitTest()); + unitTests.add(new UnitTest("<Fail>", -1)); + */ + + UnitTest [] uta = new UnitTest[unitTests.size()]; + uta = unitTests.toArray(uta); + + mListAllocs = new ScriptField_ListAllocs_s(mRS, uta.length); + for (int i = 0; i < uta.length; i++) { + ScriptField_ListAllocs_s.Item listElem = new ScriptField_ListAllocs_s.Item(); + listElem.text = Allocation.createFromString(mRS, uta[i].name); + listElem.result = uta[i].result; + mListAllocs.set(listElem, i, false); + uta[i].setItem(listElem); + } + + /* Run the actual unit tests */ + ListIterator<UnitTest> test_iter = unitTests.listIterator(); + while (test_iter.hasNext()) { + UnitTest t = test_iter.next(); + t.start(); + /* + try { + t.join(); + } catch (InterruptedException e) { + } + */ + } + + mListAllocs.copyAll(); - mRootScript = new ScriptC_test_root(mRS, mRes, R.raw.test_root, true); + mScript.bind_gList(mListAllocs); - rs_primitives_test(); - fp_mad(); + mFont = Font.createFromFamily(mRS, mRes, "serif", Font.Style.BOLD, 8); + mScript.set_gFont(mFont); + mRS.contextBindRootScript(mScript); + mRS.finish(); + } + + public void refreshTestResults() { + if (mListAllocs != null && mScript != null && mRS != null) { + mListAllocs.copyAll(); + + mScript.bind_gList(mListAllocs); + mRS.contextBindRootScript(mScript); + } } public void newTouchPosition(float x, float y, float pressure, int id) { } + + public void onActionDown(int x, int y) { + mScript.set_gDY(0.0f); + mLastX = x; + mLastY = y; + } + + public void onActionMove(int x, int y) { + int dx = mLastX - x; + int dy = mLastY - y; + + if (Math.abs(dy) <= 2) { + dy = 0; + } + + mScript.set_gDY(dy); + + mLastX = x; + mLastY = y; + } } diff --git a/libs/rs/java/tests/src/com/android/rs/test/RSTestView.java b/libs/rs/java/tests/src/com/android/rs/test/RSTestView.java index 7ae0c08..ce99c6d 100644 --- a/libs/rs/java/tests/src/com/android/rs/test/RSTestView.java +++ b/libs/rs/java/tests/src/com/android/rs/test/RSTestView.java @@ -67,40 +67,28 @@ public class RSTestView extends RSSurfaceView { } } -/* + @Override + public boolean onKeyDown(int keyCode, KeyEvent event) + { + return super.onKeyDown(keyCode, event); + } + @Override public boolean onTouchEvent(MotionEvent ev) { - int act = ev.getActionMasked(); - if (act == ev.ACTION_UP) { - mRender.newTouchPosition(0, 0, 0, ev.getPointerId(0)); - return false; - } else if (act == MotionEvent.ACTION_POINTER_UP) { - // only one pointer going up, we can get the index like this - int pointerIndex = ev.getActionIndex(); - int pointerId = ev.getPointerId(pointerIndex); - mRender.newTouchPosition(0, 0, 0, pointerId); + boolean ret = false; + int act = ev.getAction(); + if (act == ev.ACTION_DOWN) { + mRender.onActionDown((int)ev.getX(), (int)ev.getY()); + ret = true; } - int count = ev.getHistorySize(); - int pcount = ev.getPointerCount(); - - for (int p=0; p < pcount; p++) { - int id = ev.getPointerId(p); - mRender.newTouchPosition(ev.getX(p), - ev.getY(p), - ev.getPressure(p), - id); - - for (int i=0; i < count; i++) { - mRender.newTouchPosition(ev.getHistoricalX(p, i), - ev.getHistoricalY(p, i), - ev.getHistoricalPressure(p, i), - id); - } + else if (act == ev.ACTION_MOVE) { + mRender.onActionMove((int)ev.getX(), (int)ev.getY()); + ret = true; } - return true; + + return ret; } - */ } diff --git a/libs/rs/java/tests/src/com/android/rs/test/UT_fp_mad.java b/libs/rs/java/tests/src/com/android/rs/test/UT_fp_mad.java new file mode 100644 index 0000000..8391fb3 --- /dev/null +++ b/libs/rs/java/tests/src/com/android/rs/test/UT_fp_mad.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.rs.test; + +import android.content.res.Resources; +import android.renderscript.*; + +public class UT_fp_mad extends UnitTest { + private Resources mRes; + + protected UT_fp_mad(RSTestCore rstc, Resources res) { + super(rstc, "Fp_Mad"); + mRes = res; + } + + public void run() { + RenderScript pRS = RenderScript.create(); + ScriptC_fp_mad s = new ScriptC_fp_mad(pRS, mRes, R.raw.fp_mad, true); + pRS.mMessageCallback = mRsMessage; + s.invoke_fp_mad_test(0, 0); + pRS.finish(); + pRS.destroy(); + } +} + diff --git a/libs/rs/java/tests/src/com/android/rs/test/UT_primitives.java b/libs/rs/java/tests/src/com/android/rs/test/UT_primitives.java new file mode 100644 index 0000000..bef6ec5 --- /dev/null +++ b/libs/rs/java/tests/src/com/android/rs/test/UT_primitives.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.rs.test; + +import android.content.res.Resources; +import android.renderscript.*; + +public class UT_primitives extends UnitTest { + private Resources mRes; + + protected UT_primitives(RSTestCore rstc, Resources res) { + super(rstc, "Primitives"); + mRes = res; + } + + public void run() { + RenderScript pRS = RenderScript.create(); + ScriptC_primitives s = new ScriptC_primitives(pRS, mRes, R.raw.primitives, true); + pRS.mMessageCallback = mRsMessage; + s.invoke_primitives_test(0, 0); + pRS.finish(); + pRS.destroy(); + } +} + diff --git a/libs/rs/java/tests/src/com/android/rs/test/UnitTest.java b/libs/rs/java/tests/src/com/android/rs/test/UnitTest.java new file mode 100644 index 0000000..d98b763 --- /dev/null +++ b/libs/rs/java/tests/src/com/android/rs/test/UnitTest.java @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.rs.test; +import android.renderscript.RenderScript.RSMessage; + +public class UnitTest extends Thread { + public String name; + public int result; + private ScriptField_ListAllocs_s.Item mItem; + private RSTestCore mRSTC; + + /* These constants must match those in shared.rsh */ + public static final int RS_MSG_TEST_PASSED = 100; + public static final int RS_MSG_TEST_FAILED = 101; + + protected UnitTest(RSTestCore rstc, String n, int initResult) { + super(); + mRSTC = rstc; + name = n; + result = initResult; + } + + protected UnitTest(RSTestCore rstc, String n) { + this(rstc, n, 0); + } + + protected UnitTest(RSTestCore rstc) { + this (rstc, "<Unknown>"); + } + + protected UnitTest() { + this (null); + } + + protected RSMessage mRsMessage = new RSMessage() { + public void run() { + switch (mID) { + case RS_MSG_TEST_PASSED: + result = 1; + break; + case RS_MSG_TEST_FAILED: + result = -1; + break; + default: + break; + } + + if (mItem != null) { + mItem.result = result; + mRSTC.refreshTestResults(); + } + } + }; + + public void setItem(ScriptField_ListAllocs_s.Item item) { + mItem = item; + } + + public void run() { + /* This method needs to be implemented for each subclass */ + } +} + diff --git a/libs/rs/java/tests/src/com/android/rs/test/fp_mad.rs b/libs/rs/java/tests/src/com/android/rs/test/fp_mad.rs index 494ff35..eb82e56 100644 --- a/libs/rs/java/tests/src/com/android/rs/test/fp_mad.rs +++ b/libs/rs/java/tests/src/com/android/rs/test/fp_mad.rs @@ -3,8 +3,7 @@ const int TEST_COUNT = 1; #pragma rs export_var(g_results) -#pragma rs export_func(doTest) - +#pragma rs export_func(fp_mad_test) static float data_f1[1025]; static float4 data_f4[1025]; @@ -143,7 +142,7 @@ static void test_clamp4(uint32_t index) { rsDebug("fp_clamp4 M ops", 100.f / time); } -void doTest(uint32_t index, int test_num) { +void fp_mad_test(uint32_t index, int test_num) { for (int x=0; x < 1025; x++) { data_f1[x] = (x & 0xf) * 0.1f; data_f4[x].x = (x & 0xf) * 0.1f; @@ -168,6 +167,10 @@ void doTest(uint32_t index, int test_num) { test_sincos(index); test_clamp4(index); test_clamp(index); + + // TODO Actually verify test result accuracy + rsDebug("fp_mad_test PASSED", 0); + rsSendToClient(RS_MSG_TEST_PASSED); } diff --git a/libs/rs/java/tests/src/com/android/rs/test/primitives.rs b/libs/rs/java/tests/src/com/android/rs/test/primitives.rs index 39bd2c4..2ba5d52 100644 --- a/libs/rs/java/tests/src/com/android/rs/test/primitives.rs +++ b/libs/rs/java/tests/src/com/android/rs/test/primitives.rs @@ -1,10 +1,8 @@ #include "shared.rsh" -#pragma rs export_func(rs_primitives_test) +#pragma rs export_func(primitives_test) // Testing primitive types -#pragma rs export_var(floatTest) -#pragma rs export_var(doubleTest) static float floatTest = 1.99f; static double doubleTest = 2.05; static char charTest = -8; @@ -14,7 +12,7 @@ static uchar ucharTest = 8; static ushort ushortTest = 16; static uint uintTest = 32; -static void test_primitive_types(uint32_t index) { +static bool test_primitive_types(uint32_t index) { bool failed = false; start(); @@ -28,17 +26,26 @@ static void test_primitive_types(uint32_t index) { _RS_ASSERT(uintTest == 32); float time = end(index); + if (failed) { - rsDebug("test_primitives FAILED ", time); + rsDebug("test_primitives FAILED", time); } else { - rsDebug("test_primitives PASSED ", time); + rsDebug("test_primitives PASSED", time); } + + return failed; } +void primitives_test(uint32_t index, int test_num) { + bool failed = false; + failed |= test_primitive_types(index); -void rs_primitives_test(uint32_t index, int test_num) { - test_primitive_types(index); + if (failed) { + rsSendToClient(RS_MSG_TEST_FAILED); + } + else { + rsSendToClient(RS_MSG_TEST_PASSED); + } } - diff --git a/libs/rs/java/tests/src/com/android/rs/test/rslist.rs b/libs/rs/java/tests/src/com/android/rs/test/rslist.rs new file mode 100644 index 0000000..72d1850 --- /dev/null +++ b/libs/rs/java/tests/src/com/android/rs/test/rslist.rs @@ -0,0 +1,88 @@ +// 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. + +#pragma version(1) + +#pragma rs java_package_name(com.android.rs.test) + +#include "rs_graphics.rsh" + +float gDY; + +rs_font gFont; + +typedef struct ListAllocs_s { + rs_allocation text; + int result; +} ListAllocs; + +ListAllocs *gList; + +#pragma rs export_var(gDY, gFont, gList) + +void init() { + gDY = 0.0f; +} + +int textPos = 0; + +int root(int launchID) { + + rsgClearColor(0.0f, 0.0f, 0.0f, 0.0f); + rsgClearDepth(1.0f); + + textPos -= (int)gDY*2; + gDY *= 0.95; + + rsgFontColor(0.9f, 0.9f, 0.9f, 1.0f); + rsgBindFont(gFont); + color(0.2, 0.2, 0.2, 0); + + rs_allocation listAlloc = rsGetAllocation(gList); + int allocSize = rsAllocationGetDimX(listAlloc); + + int width = rsgGetWidth(); + int height = rsgGetHeight(); + + int itemHeight = 80; + int currentYPos = itemHeight + textPos; + + for(int i = 0; i < allocSize; i ++) { + if(currentYPos - itemHeight > height) { + break; + } + + if(currentYPos > 0) { + switch(gList[i].result) { + case 1: /* Passed */ + rsgFontColor(0.5f, 0.9f, 0.5f, 1.0f); + break; + case -1: /* Failed */ + rsgFontColor(0.9f, 0.5f, 0.5f, 1.0f); + break; + case 0: /* Still Testing */ + rsgFontColor(0.9f, 0.9f, 0.5f, 1.0f); + break; + default: /* Unknown */ + rsgFontColor(0.9f, 0.9f, 0.9f, 1.0f); + break; + } + rsgDrawRect(0, currentYPos - 1, width, currentYPos, 0); + rsgDrawText(gList[i].text, 30, currentYPos - 32); + } + currentYPos += itemHeight; + } + + return 10; +} diff --git a/libs/rs/java/tests/src/com/android/rs/test/shared.rsh b/libs/rs/java/tests/src/com/android/rs/test/shared.rsh index 8c3e5f4..820fffd 100644 --- a/libs/rs/java/tests/src/com/android/rs/test/shared.rsh +++ b/libs/rs/java/tests/src/com/android/rs/test/shared.rsh @@ -25,7 +25,6 @@ static float end(uint32_t idx) { #define _RS_ASSERT(b) \ do { \ - rsDebug("Checking " #b, ((int) (b))); \ if (!(b)) { \ failed = true; \ rsDebug(#b " FAILED", 0); \ @@ -33,3 +32,7 @@ do { \ \ } while (0) +/* These constants must match those in UnitTest.java */ +static const int RS_MSG_TEST_PASSED = 100; +static const int RS_MSG_TEST_FAILED = 101; + diff --git a/libs/rs/rs.spec b/libs/rs/rs.spec index 21cbc50..2b7928f 100644 --- a/libs/rs/rs.spec +++ b/libs/rs/rs.spec @@ -434,12 +434,6 @@ ProgramBindSampler { } ProgramFragmentCreate { - param const uint32_t * params - param uint32_t paramLength - ret RsProgramFragment - } - -ProgramFragmentCreate2 { param const char * shaderText param uint32_t shaderLength param const uint32_t * params @@ -448,11 +442,6 @@ ProgramFragmentCreate2 { } ProgramVertexCreate { - param bool texMat - ret RsProgramVertex - } - -ProgramVertexCreate2 { param const char * shaderText param uint32_t shaderLength param const uint32_t * params diff --git a/libs/rs/rsContext.cpp b/libs/rs/rsContext.cpp index 3dbdbfb..3681bc2 100644 --- a/libs/rs/rsContext.cpp +++ b/libs/rs/rsContext.cpp @@ -163,11 +163,6 @@ uint32_t Context::runRootScript() uint32_t ret = runScript(mRootScript.get()); checkError("runRootScript"); - if (mError != RS_ERROR_NONE) { - // If we have an error condition we stop rendering until - // somthing changes that might fix it. - ret = 0; - } return ret; } diff --git a/libs/rs/rsContext.h b/libs/rs/rsContext.h index e38ba55..bce9c13 100644 --- a/libs/rs/rsContext.h +++ b/libs/rs/rsContext.h @@ -169,6 +169,9 @@ public: mutable const ObjectBase * mObjHead; bool ext_OES_texture_npot() const {return mGL.OES_texture_npot;} + uint32_t getMaxFragmentTextures() const {return mGL.mMaxFragmentTextureImageUnits;} + uint32_t getMaxFragmentUniformVectors() const {return mGL.mMaxFragmentUniformVectors;} + uint32_t getMaxVertexUniformVectors() const {return mGL.mMaxVertexUniformVectors;} void launchThreads(WorkerCallback_t cbk, void *data); uint32_t getWorkerPoolSize() const {return (uint32_t)mWorkers.mRunningCount;} diff --git a/libs/rs/rsContextHostStub.h b/libs/rs/rsContextHostStub.h index c437606..f30915e 100644 --- a/libs/rs/rsContextHostStub.h +++ b/libs/rs/rsContextHostStub.h @@ -120,6 +120,9 @@ public: mutable const ObjectBase * mObjHead; bool ext_OES_texture_npot() const {return mGL.OES_texture_npot;} + uint32_t getMaxFragmentTextures() const {return mGL.mMaxFragmentTextureImageUnits;} + uint32_t getMaxFragmentUniformVectors() const {return mGL.mMaxFragmentUniformVectors;} + uint32_t getMaxVertexUniformVectors() const {return mGL.mMaxVertexUniformVectors;} protected: diff --git a/libs/rs/rsElement.cpp b/libs/rs/rsElement.cpp index d0909c8..0b9e28c 100644 --- a/libs/rs/rsElement.cpp +++ b/libs/rs/rsElement.cpp @@ -138,37 +138,10 @@ Element *Element::createFromStream(Context *rsc, IStream *stream) // We need to check if this already exists for (uint32_t ct=0; ct < rsc->mStateElement.mElements.size(); ct++) { Element *ee = rsc->mStateElement.mElements[ct]; - - if (!ee->getFieldCount() ) { - - if((ee->getComponent().getType() == elem->getComponent().getType()) && - (ee->getComponent().getKind() == elem->getComponent().getKind()) && - (ee->getComponent().getIsNormalized() == elem->getComponent().getIsNormalized()) && - (ee->getComponent().getVectorSize() == elem->getComponent().getVectorSize())) { - // Match - delete elem; - ee->incUserRef(); - return ee; - } - - } else if (ee->getFieldCount() == elem->mFieldCount) { - - bool match = true; - for (uint32_t i=0; i < elem->mFieldCount; i++) { - if ((ee->mFields[i].e.get() != elem->mFields[i].e.get()) || - (ee->mFields[i].name.length() != elem->mFields[i].name.length()) || - (ee->mFields[i].name != elem->mFields[i].name) || - (ee->mFields[i].arraySize != elem->mFields[i].arraySize)) { - match = false; - break; - } - } - if (match) { - delete elem; - ee->incUserRef(); - return ee; - } - + if(ee->isEqual(elem)) { + delete elem; + ee->incUserRef(); + return ee; } } @@ -176,6 +149,32 @@ Element *Element::createFromStream(Context *rsc, IStream *stream) return elem; } +bool Element::isEqual(const Element *other) const { + if(other == NULL) { + return false; + } + if (!other->getFieldCount() && !mFieldCount) { + if((other->getType() == getType()) && + (other->getKind() == getKind()) && + (other->getComponent().getIsNormalized() == getComponent().getIsNormalized()) && + (other->getComponent().getVectorSize() == getComponent().getVectorSize())) { + return true; + } + return false; + } + if (other->getFieldCount() == mFieldCount) { + for (uint32_t i=0; i < mFieldCount; i++) { + if ((!other->mFields[i].e->isEqual(mFields[i].e.get())) || + (other->mFields[i].name.length() != mFields[i].name.length()) || + (other->mFields[i].name != mFields[i].name) || + (other->mFields[i].arraySize != mFields[i].arraySize)) { + return false; + } + } + return true; + } + return false; +} const Element * Element::create(Context *rsc, RsDataType dt, RsDataKind dk, bool isNorm, uint32_t vecSize) diff --git a/libs/rs/rsElement.h b/libs/rs/rsElement.h index ae6a6cc..50bca85 100644 --- a/libs/rs/rsElement.h +++ b/libs/rs/rsElement.h @@ -72,6 +72,8 @@ public: void decRefs(const void *) const; bool getHasReferences() const {return mHasReference;} + bool isEqual(const Element *other) const; + protected: // deallocate any components that are part of this element. void clear(); diff --git a/libs/rs/rsFileA3D.cpp b/libs/rs/rsFileA3D.cpp index 5709f2a..893598f 100644 --- a/libs/rs/rsFileA3D.cpp +++ b/libs/rs/rsFileA3D.cpp @@ -68,13 +68,11 @@ void FileA3D::parseHeader(IStream *headerStream) uint32_t flags = headerStream->loadU32(); mUse64BitOffsets = (flags & 1) != 0; - LOGE("file open 64bit = %i", mUse64BitOffsets); - uint32_t numIndexEntries = headerStream->loadU32(); for(uint32_t i = 0; i < numIndexEntries; i ++) { A3DIndexEntry *entry = new A3DIndexEntry(); headerStream->loadString(&entry->mObjectName); - LOGE("Header data, entry name = %s", entry->mObjectName.string()); + LOGV("Header data, entry name = %s", entry->mObjectName.string()); entry->mType = (RsA3DClassID)headerStream->loadU32(); if(mUse64BitOffsets){ entry->mOffset = headerStream->loadOffset(); @@ -91,7 +89,6 @@ void FileA3D::parseHeader(IStream *headerStream) bool FileA3D::load(const void *data, size_t length) { - LOGE("Loading data. Size: %u", length); const uint8_t *localData = (const uint8_t *)data; size_t lengthRemaining = length; @@ -114,8 +111,6 @@ bool FileA3D::load(const void *data, size_t length) localData += sizeof(headerSize); lengthRemaining -= sizeof(headerSize); - LOGE("Loading data, headerSize = %lli", headerSize); - if(lengthRemaining < headerSize) { return false; } @@ -145,8 +140,6 @@ bool FileA3D::load(const void *data, size_t length) localData += sizeof(mDataSize); lengthRemaining -= sizeof(mDataSize); - LOGE("Loading data, mDataSize = %lli", mDataSize); - if(lengthRemaining < mDataSize) { return false; } @@ -169,7 +162,7 @@ bool FileA3D::load(FILE *f) char magicString[12]; size_t len; - LOGE("file open 1"); + LOGV("file open 1"); len = fread(magicString, 1, 12, f); if ((len != 12) || memcmp(magicString, "Android3D_ff", 12)) { @@ -205,7 +198,7 @@ bool FileA3D::load(FILE *f) return false; } - LOGE("file open size = %lli", mDataSize); + LOGV("file open size = %lli", mDataSize); // We should know enough to read the file in at this point. mAlloc = malloc(mDataSize); @@ -220,7 +213,7 @@ bool FileA3D::load(FILE *f) mReadStream = new IStream(mData, mUse64BitOffsets); - LOGE("Header is read an stream initialized"); + LOGV("Header is read an stream initialized"); return true; } @@ -437,7 +430,7 @@ RsObjectBase rsi_FileA3DGetEntryByIndex(Context *rsc, uint32_t index, RsFile fil } ObjectBase *obj = fa3d->initializeFromEntry(index); - LOGE("Returning object with name %s", obj->getName()); + LOGV("Returning object with name %s", obj->getName()); return obj; } diff --git a/libs/rs/rsFont.cpp b/libs/rs/rsFont.cpp index 0f815a2..bd5713e 100644 --- a/libs/rs/rsFont.cpp +++ b/libs/rs/rsFont.cpp @@ -372,7 +372,7 @@ bool FontState::cacheBitmap(FT_Bitmap *bitmap, uint32_t *retOriginX, uint32_t *r // This will dirty the texture and the shader so next time // we draw it will upload the data mTextTexture->deferedUploadToTexture(mRSC, false, 0); - mFontShaderF->bindTexture(0, mTextTexture.get()); + mFontShaderF->bindTexture(mRSC, 0, mTextTexture.get()); // Some debug code /*for(uint32_t i = 0; i < mCacheLines.size(); i ++) { @@ -414,12 +414,12 @@ void FontState::initRenderState() ProgramFragment *pf = new ProgramFragment(mRSC, shaderString.string(), shaderString.length(), tmp, 4); mFontShaderF.set(pf); - mFontShaderF->bindAllocation(mFontShaderFConstant.get(), 0); + mFontShaderF->bindAllocation(mRSC, mFontShaderFConstant.get(), 0); Sampler *sampler = new Sampler(mRSC, RS_SAMPLER_NEAREST, RS_SAMPLER_NEAREST, RS_SAMPLER_CLAMP, RS_SAMPLER_CLAMP, RS_SAMPLER_CLAMP); mFontSampler.set(sampler); - mFontShaderF->bindSampler(0, sampler); + mFontShaderF->bindSampler(mRSC, 0, sampler); ProgramStore *fontStore = new ProgramStore(mRSC); mFontProgramStore.set(fontStore); diff --git a/libs/rs/rsProgram.cpp b/libs/rs/rsProgram.cpp index 2441491..9c66462 100644 --- a/libs/rs/rsProgram.cpp +++ b/libs/rs/rsProgram.cpp @@ -29,7 +29,6 @@ using namespace android; using namespace android::renderscript; - Program::Program(Context *rsc) : ObjectBase(rsc) { mAllocFile = __FILE__; @@ -38,7 +37,10 @@ Program::Program(Context *rsc) : ObjectBase(rsc) mShaderID = 0; mAttribCount = 0; mUniformCount = 0; + mTextureCount = 0; + mTextures = NULL; + mSamplers = NULL; mInputElements = NULL; mOutputElements = NULL; mConstantTypes = NULL; @@ -80,6 +82,8 @@ Program::Program(Context *rsc, const char * shaderText, uint32_t shaderLength, } } + mTextures = new ObjectBaseRef<Allocation>[mTextureCount]; + mSamplers = new ObjectBaseRef<Sampler>[mTextureCount]; mInputElements = new ObjectBaseRef<Element>[mInputCount]; mOutputElements = new ObjectBaseRef<Element>[mOutputCount]; mConstantTypes = new ObjectBaseRef<Type>[mConstantCount]; @@ -112,9 +116,15 @@ Program::Program(Context *rsc, const char * shaderText, uint32_t shaderLength, Program::~Program() { for (uint32_t ct=0; ct < MAX_UNIFORMS; ct++) { - bindAllocation(NULL, ct); + bindAllocation(NULL, NULL, ct); } + for (uint32_t ct=0; ct < mTextureCount; ct++) { + bindTexture(NULL, ct, NULL); + bindSampler(NULL, ct, NULL); + } + delete[] mTextures; + delete[] mSamplers; delete[] mInputElements; delete[] mOutputElements; delete[] mConstantTypes; @@ -124,8 +134,22 @@ Program::~Program() } -void Program::bindAllocation(Allocation *alloc, uint32_t slot) +void Program::bindAllocation(Context *rsc, Allocation *alloc, uint32_t slot) { + if (alloc != NULL) { + if (slot >= mConstantCount) { + LOGE("Attempt to bind alloc at slot %u, on shader id %u, but const count is %u", + slot, (uint32_t)this, mConstantCount); + rsc->setError(RS_ERROR_BAD_SHADER, "Cannot bind allocation"); + return; + } + if (!alloc->getType()->isEqual(mConstantTypes[slot].get())) { + LOGE("Attempt to bind alloc at slot %u, on shader id %u, but types mismatch", + slot, (uint32_t)this); + rsc->setError(RS_ERROR_BAD_SHADER, "Cannot bind allocation"); + return; + } + } if (mConstants[slot].get() == alloc) { return; } @@ -139,10 +163,11 @@ void Program::bindAllocation(Allocation *alloc, uint32_t slot) mDirty = true; } -void Program::bindTexture(uint32_t slot, Allocation *a) +void Program::bindTexture(Context *rsc, uint32_t slot, Allocation *a) { - if (slot >= MAX_TEXTURE) { - LOGE("Attempt to bind a texture to a slot > MAX_TEXTURE"); + if (slot >= mTextureCount) { + LOGE("Attempt to bind texture to slot %u but tex count is %u", slot, mTextureCount); + rsc->setError(RS_ERROR_BAD_SHADER, "Cannot bind texture"); return; } @@ -151,10 +176,11 @@ void Program::bindTexture(uint32_t slot, Allocation *a) mDirty = true; } -void Program::bindSampler(uint32_t slot, Sampler *s) +void Program::bindSampler(Context *rsc, uint32_t slot, Sampler *s) { - if (slot >= MAX_TEXTURE) { - LOGE("Attempt to bind a Sampler to a slot > MAX_TEXTURE"); + if (slot >= mTextureCount) { + LOGE("Attempt to bind sampler to slot %u but tex count is %u", slot, mTextureCount); + rsc->setError(RS_ERROR_BAD_SHADER, "Cannot bind sampler"); return; } @@ -289,11 +315,13 @@ void Program::appendUserConstants() { } } -void Program::setupUserConstants(ShaderCache *sc, bool isFragment) { +void Program::setupUserConstants(Context *rsc, ShaderCache *sc, bool isFragment) { uint32_t uidx = 0; for (uint32_t ct=0; ct < mConstantCount; ct++) { Allocation *alloc = mConstants[ct].get(); if (!alloc) { + LOGE("Attempting to set constants on shader id %u, but alloc at slot %u is not set", (uint32_t)this, ct); + rsc->setError(RS_ERROR_BAD_SHADER, "No constant allocation bound"); continue; } @@ -384,19 +412,19 @@ namespace renderscript { void rsi_ProgramBindConstants(Context *rsc, RsProgram vp, uint32_t slot, RsAllocation constants) { Program *p = static_cast<Program *>(vp); - p->bindAllocation(static_cast<Allocation *>(constants), slot); + p->bindAllocation(rsc, static_cast<Allocation *>(constants), slot); } void rsi_ProgramBindTexture(Context *rsc, RsProgram vpf, uint32_t slot, RsAllocation a) { Program *p = static_cast<Program *>(vpf); - p->bindTexture(slot, static_cast<Allocation *>(a)); + p->bindTexture(rsc, slot, static_cast<Allocation *>(a)); } void rsi_ProgramBindSampler(Context *rsc, RsProgram vpf, uint32_t slot, RsSampler s) { Program *p = static_cast<Program *>(vpf); - p->bindSampler(slot, static_cast<Sampler *>(s)); + p->bindSampler(rsc, slot, static_cast<Sampler *>(s)); } } diff --git a/libs/rs/rsProgram.h b/libs/rs/rsProgram.h index e7329c2..a8f34c3 100644 --- a/libs/rs/rsProgram.h +++ b/libs/rs/rsProgram.h @@ -32,20 +32,19 @@ class Program : public ObjectBase public: const static uint32_t MAX_ATTRIBS = 8; const static uint32_t MAX_UNIFORMS = 16; - const static uint32_t MAX_TEXTURE = 2; Program(Context *); Program(Context *, const char * shaderText, uint32_t shaderLength, const uint32_t * params, uint32_t paramLength); virtual ~Program(); - void bindAllocation(Allocation *, uint32_t slot); + void bindAllocation(Context *, Allocation *, uint32_t slot); virtual void createShader(); bool isUserProgram() const {return !mIsInternal;} - void bindTexture(uint32_t slot, Allocation *); - void bindSampler(uint32_t slot, Sampler *); + void bindTexture(Context *, uint32_t slot, Allocation *); + void bindSampler(Context *, uint32_t slot, Sampler *); uint32_t getShaderID() const {return mShaderID;} void setShader(const char *, uint32_t len); @@ -75,7 +74,7 @@ protected: // Applies to vertex and fragment shaders only void appendUserConstants(); - void setupUserConstants(ShaderCache *sc, bool isFragment); + void setupUserConstants(Context *rsc, ShaderCache *sc, bool isFragment); void initAddUserElement(const Element *e, String8 *names, uint32_t *count, const char *prefix); ObjectBaseRef<Allocation> mConstants[MAX_UNIFORMS]; @@ -97,8 +96,8 @@ protected: // and filtered. // // Constants are strictly accessed by programetic loads. - ObjectBaseRef<Allocation> mTextures[MAX_TEXTURE]; - ObjectBaseRef<Sampler> mSamplers[MAX_TEXTURE]; + ObjectBaseRef<Allocation> *mTextures; + ObjectBaseRef<Sampler> *mSamplers; bool loadShader(Context *, uint32_t type); diff --git a/libs/rs/rsProgramFragment.cpp b/libs/rs/rsProgramFragment.cpp index 8f5c653..d511d21 100644 --- a/libs/rs/rsProgramFragment.cpp +++ b/libs/rs/rsProgramFragment.cpp @@ -31,40 +31,6 @@ using namespace android; using namespace android::renderscript; - -ProgramFragment::ProgramFragment(Context *rsc, const uint32_t * params, - uint32_t paramLength) : - Program(rsc) -{ - mAllocFile = __FILE__; - mAllocLine = __LINE__; - rsAssert(paramLength == 6); - - mConstantColor[0] = 1.f; - mConstantColor[1] = 1.f; - mConstantColor[2] = 1.f; - mConstantColor[3] = 1.f; - - mEnvModes[0] = (RsTexEnvMode)params[0]; - mTextureFormats[0] = params[1]; - mEnvModes[1] = (RsTexEnvMode)params[2]; - mTextureFormats[1] = params[3]; - mPointSpriteEnable = params[4] != 0; - mVaryingColor = false; - if (paramLength > 5) - mVaryingColor = params[5] != 0; - - mTextureEnableMask = 0; - if (mEnvModes[0]) { - mTextureEnableMask |= 1; - } - if (mEnvModes[1]) { - mTextureEnableMask |= 2; - } - - init(rsc); -} - ProgramFragment::ProgramFragment(Context *rsc, const char * shaderText, uint32_t shaderLength, const uint32_t * params, uint32_t paramLength) : @@ -78,19 +44,23 @@ ProgramFragment::ProgramFragment(Context *rsc, const char * shaderText, mConstantColor[2] = 1.f; mConstantColor[3] = 1.f; - mTextureEnableMask = (1 << mTextureCount) -1; - init(rsc); } - ProgramFragment::~ProgramFragment() { } -void ProgramFragment::setConstantColor(float r, float g, float b, float a) +void ProgramFragment::setConstantColor(Context *rsc, float r, float g, float b, float a) { if(isUserProgram()) { + LOGE("Attempting to set fixed function emulation color on user program"); + rsc->setError(RS_ERROR_BAD_SHADER, "Cannot set fixed function emulation color on user program"); + return; + } + if(mConstants[0].get() == NULL) { + LOGE("Unable to set fixed function emulation color because allocation is missing"); + rsc->setError(RS_ERROR_BAD_SHADER, "Unable to set fixed function emulation color because allocation is missing"); return; } mConstantColor[0] = r; @@ -101,7 +71,7 @@ void ProgramFragment::setConstantColor(float r, float g, float b, float a) mDirty = true; } -void ProgramFragment::setupGL2(const Context *rsc, ProgramFragmentState *state, ShaderCache *sc) +void ProgramFragment::setupGL2(Context *rsc, ProgramFragmentState *state, ShaderCache *sc) { //LOGE("sgl2 frag1 %x", glGetError()); if ((state->mLast.get() == this) && !mDirty) { @@ -112,11 +82,22 @@ void ProgramFragment::setupGL2(const Context *rsc, ProgramFragmentState *state, rsc->checkError("ProgramFragment::setupGL2 start"); rsc->checkError("ProgramFragment::setupGL2 begin uniforms"); - setupUserConstants(sc, true); + setupUserConstants(rsc, sc, true); + + uint32_t numTexturesToBind = mTextureCount; + uint32_t numTexturesAvailable = rsc->getMaxFragmentTextures(); + if(numTexturesToBind >= numTexturesAvailable) { + LOGE("Attempting to bind %u textures on shader id %u, but only %u are available", + mTextureCount, (uint32_t)this, numTexturesAvailable); + rsc->setError(RS_ERROR_BAD_SHADER, "Cannot bind more textuers than available"); + numTexturesToBind = numTexturesAvailable; + } - for (uint32_t ct=0; ct < MAX_TEXTURE; ct++) { + for (uint32_t ct=0; ct < numTexturesToBind; ct++) { glActiveTexture(GL_TEXTURE0 + ct); - if (!(mTextureEnableMask & (1 << ct)) || !mTextures[ct].get()) { + if (!mTextures[ct].get()) { + LOGE("No texture bound for shader id %u, texture unit %u", (uint)this, ct); + rsc->setError(RS_ERROR_BAD_SHADER, "No texture bound"); continue; } @@ -151,8 +132,8 @@ void ProgramFragment::createShader() if (mUserShader.length() > 1) { mShader.append("precision mediump float;\n"); appendUserConstants(); + char buf[256]; for (uint32_t ct=0; ct < mTextureCount; ct++) { - char buf[256]; sprintf(buf, "uniform sampler2D UNI_Tex%i;\n", ct); mShader.append(buf); } @@ -172,8 +153,11 @@ void ProgramFragment::init(Context *rsc) } } mTextureUniformIndexStart = mUniformCount; - mUniformNames[mUniformCount++].setTo("UNI_Tex0"); - mUniformNames[mUniformCount++].setTo("UNI_Tex1"); + char buf[256]; + for (uint32_t ct=0; ct < mTextureCount; ct++) { + sprintf(buf, "UNI_Tex%i", ct); + mUniformNames[mUniformCount++].setTo(buf); + } createShader(); } @@ -228,8 +212,8 @@ void ProgramFragmentState::init(Context *rsc) Allocation *constAlloc = new Allocation(rsc, inputType); ProgramFragment *pf = new ProgramFragment(rsc, shaderString.string(), shaderString.length(), tmp, 4); - pf->bindAllocation(constAlloc, 0); - pf->setConstantColor(1.0f, 1.0f, 1.0f, 1.0f); + pf->bindAllocation(rsc, constAlloc, 0); + pf->setConstantColor(rsc, 1.0f, 1.0f, 1.0f, 1.0f); mDefault.set(pf); } @@ -244,23 +228,13 @@ void ProgramFragmentState::deinit(Context *rsc) namespace android { namespace renderscript { -RsProgramFragment rsi_ProgramFragmentCreate(Context *rsc, - const uint32_t * params, - uint32_t paramLength) -{ - ProgramFragment *pf = new ProgramFragment(rsc, params, paramLength); - pf->incUserRef(); - //LOGE("rsi_ProgramFragmentCreate %p", pf); - return pf; -} - -RsProgramFragment rsi_ProgramFragmentCreate2(Context *rsc, const char * shaderText, +RsProgramFragment rsi_ProgramFragmentCreate(Context *rsc, const char * shaderText, uint32_t shaderLength, const uint32_t * params, uint32_t paramLength) { ProgramFragment *pf = new ProgramFragment(rsc, shaderText, shaderLength, params, paramLength); pf->incUserRef(); - //LOGE("rsi_ProgramFragmentCreate2 %p", pf); + //LOGE("rsi_ProgramFragmentCreate %p", pf); return pf; } diff --git a/libs/rs/rsProgramFragment.h b/libs/rs/rsProgramFragment.h index fb78b3f..1cf9ca7 100644 --- a/libs/rs/rsProgramFragment.h +++ b/libs/rs/rsProgramFragment.h @@ -28,13 +28,12 @@ class ProgramFragmentState; class ProgramFragment : public Program { public: - ProgramFragment(Context *, const uint32_t * params, uint32_t paramLength); ProgramFragment(Context *rsc, const char * shaderText, uint32_t shaderLength, const uint32_t * params, uint32_t paramLength); virtual ~ProgramFragment(); - virtual void setupGL2(const Context *, ProgramFragmentState *, ShaderCache *sc); + virtual void setupGL2(Context *, ProgramFragmentState *, ShaderCache *sc); virtual void createShader(); virtual void loadShader(Context *rsc); @@ -43,19 +42,10 @@ public: virtual RsA3DClassID getClassId() const { return RS_A3D_CLASS_ID_PROGRAM_FRAGMENT; } static ProgramFragment *createFromStream(Context *rsc, IStream *stream); - void setConstantColor(float, float, float, float); + void setConstantColor(Context *, float, float, float, float); protected: - // Hacks to create a program for now - uint32_t mTextureFormats[MAX_TEXTURE]; - uint32_t mTextureDimensions[MAX_TEXTURE]; - RsTexEnvMode mEnvModes[MAX_TEXTURE]; - uint32_t mTextureEnableMask; - bool mPointSpriteEnable; - bool mVaryingColor; - float mConstantColor[4]; - int32_t mConstantColorUniformIndex; int32_t mTextureUniformIndexStart; }; @@ -69,7 +59,6 @@ public: void init(Context *rsc); void deinit(Context *rsc); - ObjectBaseRef<Type> mTextureTypes[ProgramFragment::MAX_TEXTURE]; ObjectBaseRef<ProgramFragment> mDefault; Vector<ProgramFragment *> mPrograms; diff --git a/libs/rs/rsProgramVertex.cpp b/libs/rs/rsProgramVertex.cpp index 6446b55..c3ef356 100644 --- a/libs/rs/rsProgramVertex.cpp +++ b/libs/rs/rsProgramVertex.cpp @@ -32,16 +32,6 @@ using namespace android; using namespace android::renderscript; -ProgramVertex::ProgramVertex(Context *rsc, bool texMat) : - Program(rsc) -{ - mAllocFile = __FILE__; - mAllocLine = __LINE__; - mTextureMatrixEnable = texMat; - mLightCount = 0; - init(rsc); -} - ProgramVertex::ProgramVertex(Context *rsc, const char * shaderText, uint32_t shaderLength, const uint32_t * params, uint32_t paramLength) : @@ -49,8 +39,6 @@ ProgramVertex::ProgramVertex(Context *rsc, const char * shaderText, { mAllocFile = __FILE__; mAllocLine = __LINE__; - mTextureMatrixEnable = false; - mLightCount = 0; init(rsc); } @@ -110,7 +98,7 @@ void ProgramVertex::createShader() } } -void ProgramVertex::setupGL2(const Context *rsc, ProgramVertexState *state, ShaderCache *sc) +void ProgramVertex::setupGL2(Context *rsc, ProgramVertexState *state, ShaderCache *sc) { //LOGE("sgl2 vtx1 %x", glGetError()); if ((state->mLast.get() == this) && !mDirty) { @@ -132,23 +120,21 @@ void ProgramVertex::setupGL2(const Context *rsc, ProgramVertexState *state, Shad } rsc->checkError("ProgramVertex::setupGL2 begin uniforms"); - setupUserConstants(sc, false); + setupUserConstants(rsc, sc, false); state->mLast.set(this); rsc->checkError("ProgramVertex::setupGL2"); } -void ProgramVertex::addLight(const Light *l) -{ - if (mLightCount < MAX_LIGHTS) { - mLights[mLightCount].set(l); - mLightCount++; - } -} - -void ProgramVertex::setProjectionMatrix(const rsc_Matrix *m) const +void ProgramVertex::setProjectionMatrix(Context *rsc, const rsc_Matrix *m) const { if(isUserProgram()) { + LOGE("Attempting to set fixed function emulation matrix projection on user program"); + rsc->setError(RS_ERROR_BAD_SHADER, "Cannot set emulation matrix on user shader"); + return; + } + if(mConstants[0].get() == NULL) { + LOGE("Unable to set fixed function emulation matrix projection because allocation is missing"); return; } float *f = static_cast<float *>(mConstants[0]->getPtr()); @@ -156,9 +142,16 @@ void ProgramVertex::setProjectionMatrix(const rsc_Matrix *m) const mDirty = true; } -void ProgramVertex::setModelviewMatrix(const rsc_Matrix *m) const +void ProgramVertex::setModelviewMatrix(Context *rsc, const rsc_Matrix *m) const { if(isUserProgram()) { + LOGE("Attempting to set fixed function emulation matrix modelview on user program"); + rsc->setError(RS_ERROR_BAD_SHADER, "Cannot set emulation matrix on user shader"); + return; + } + if(mConstants[0].get() == NULL) { + LOGE("Unable to set fixed function emulation matrix modelview because allocation is missing"); + rsc->setError(RS_ERROR_BAD_SHADER, "Fixed function allocation missing"); return; } float *f = static_cast<float *>(mConstants[0]->getPtr()); @@ -166,9 +159,16 @@ void ProgramVertex::setModelviewMatrix(const rsc_Matrix *m) const mDirty = true; } -void ProgramVertex::setTextureMatrix(const rsc_Matrix *m) const +void ProgramVertex::setTextureMatrix(Context *rsc, const rsc_Matrix *m) const { if(isUserProgram()) { + LOGE("Attempting to set fixed function emulation matrix texture on user program"); + rsc->setError(RS_ERROR_BAD_SHADER, "Cannot set emulation matrix on user shader"); + return; + } + if(mConstants[0].get() == NULL) { + LOGE("Unable to set fixed function emulation matrix texture because allocation is missing"); + rsc->setError(RS_ERROR_BAD_SHADER, "Fixed function allocation missing"); return; } float *f = static_cast<float *>(mConstants[0]->getPtr()); @@ -176,16 +176,23 @@ void ProgramVertex::setTextureMatrix(const rsc_Matrix *m) const mDirty = true; } -void ProgramVertex::getProjectionMatrix(rsc_Matrix *m) const +void ProgramVertex::getProjectionMatrix(Context *rsc, rsc_Matrix *m) const { if(isUserProgram()) { + LOGE("Attempting to get fixed function emulation matrix projection on user program"); + rsc->setError(RS_ERROR_BAD_SHADER, "Cannot get emulation matrix on user shader"); + return; + } + if(mConstants[0].get() == NULL) { + LOGE("Unable to get fixed function emulation matrix projection because allocation is missing"); + rsc->setError(RS_ERROR_BAD_SHADER, "Fixed function allocation missing"); return; } float *f = static_cast<float *>(mConstants[0]->getPtr()); memcpy(m, &f[RS_PROGRAM_VERTEX_PROJECTION_OFFSET], sizeof(rsc_Matrix)); } -void ProgramVertex::transformToScreen(const Context *rsc, float *v4out, const float *v3in) const +void ProgramVertex::transformToScreen(Context *rsc, float *v4out, const float *v3in) const { if(isUserProgram()) { return; @@ -280,11 +287,10 @@ void ProgramVertexState::init(Context *rsc) ProgramVertex *pv = new ProgramVertex(rsc, shaderString.string(), shaderString.length(), tmp, 6); Allocation *alloc = new Allocation(rsc, inputType); - pv->bindAllocation(alloc, 0); + pv->bindAllocation(rsc, alloc, 0); mDefaultAlloc.set(alloc); mDefault.set(pv); - pv->bindAllocation(alloc, 0); updateSize(rsc); @@ -315,15 +321,7 @@ void ProgramVertexState::deinit(Context *rsc) namespace android { namespace renderscript { - -RsProgramVertex rsi_ProgramVertexCreate(Context *rsc, bool texMat) -{ - ProgramVertex *pv = new ProgramVertex(rsc, texMat); - pv->incUserRef(); - return pv; -} - -RsProgramVertex rsi_ProgramVertexCreate2(Context *rsc, const char * shaderText, +RsProgramVertex rsi_ProgramVertexCreate(Context *rsc, const char * shaderText, uint32_t shaderLength, const uint32_t * params, uint32_t paramLength) { diff --git a/libs/rs/rsProgramVertex.h b/libs/rs/rsProgramVertex.h index 65ce541..355df2b 100644 --- a/libs/rs/rsProgramVertex.h +++ b/libs/rs/rsProgramVertex.h @@ -28,25 +28,18 @@ class ProgramVertexState; class ProgramVertex : public Program { public: - const static uint32_t MAX_LIGHTS = 8; - ProgramVertex(Context *,const char * shaderText, uint32_t shaderLength, const uint32_t * params, uint32_t paramLength); - ProgramVertex(Context *, bool texMat); virtual ~ProgramVertex(); - virtual void setupGL2(const Context *rsc, ProgramVertexState *state, ShaderCache *sc); - - - void setTextureMatrixEnable(bool e) {mTextureMatrixEnable = e;} - void addLight(const Light *); + virtual void setupGL2(Context *rsc, ProgramVertexState *state, ShaderCache *sc); - void setProjectionMatrix(const rsc_Matrix *) const; - void getProjectionMatrix(rsc_Matrix *) const; - void setModelviewMatrix(const rsc_Matrix *) const; - void setTextureMatrix(const rsc_Matrix *) const; + void setProjectionMatrix(Context *, const rsc_Matrix *) const; + void getProjectionMatrix(Context *, rsc_Matrix *) const; + void setModelviewMatrix(Context *, const rsc_Matrix *) const; + void setTextureMatrix(Context *, const rsc_Matrix *) const; - void transformToScreen(const Context *, float *v4out, const float *v3in) const; + void transformToScreen(Context *, float *v4out, const float *v3in) const; virtual void createShader(); virtual void loadShader(Context *); @@ -55,13 +48,6 @@ public: virtual void serialize(OStream *stream) const; virtual RsA3DClassID getClassId() const { return RS_A3D_CLASS_ID_PROGRAM_VERTEX; } static ProgramVertex *createFromStream(Context *rsc, IStream *stream); - -protected: - uint32_t mLightCount; - ObjectBaseRef<const Light> mLights[MAX_LIGHTS]; - - // Hacks to create a program for now - bool mTextureMatrixEnable; }; diff --git a/libs/rs/rsScriptC_LibGL.cpp b/libs/rs/rsScriptC_LibGL.cpp index 2f8f79a..4be5059 100644 --- a/libs/rs/rsScriptC_LibGL.cpp +++ b/libs/rs/rsScriptC_LibGL.cpp @@ -93,33 +93,33 @@ static void SC_bindProgramRaster(RsProgramRaster pv) static void SC_vpLoadProjectionMatrix(const rsc_Matrix *m) { GET_TLS(); - rsc->getVertex()->setProjectionMatrix(m); + rsc->getVertex()->setProjectionMatrix(rsc, m); } static void SC_vpLoadModelMatrix(const rsc_Matrix *m) { GET_TLS(); - rsc->getVertex()->setModelviewMatrix(m); + rsc->getVertex()->setModelviewMatrix(rsc, m); } static void SC_vpLoadTextureMatrix(const rsc_Matrix *m) { GET_TLS(); - rsc->getVertex()->setTextureMatrix(m); + rsc->getVertex()->setTextureMatrix(rsc, m); } static void SC_pfConstantColor(RsProgramFragment vpf, float r, float g, float b, float a) { - //GET_TLS(); + GET_TLS(); ProgramFragment *pf = static_cast<ProgramFragment *>(vpf); - pf->setConstantColor(r, g, b, a); + pf->setConstantColor(rsc, r, g, b, a); } static void SC_vpGetProjectionMatrix(rsc_Matrix *m) { GET_TLS(); - rsc->getVertex()->getProjectionMatrix(m); + rsc->getVertex()->getProjectionMatrix(rsc, m); } @@ -280,7 +280,7 @@ static void SC_color(float r, float g, float b, float a) { GET_TLS(); ProgramFragment *pf = (ProgramFragment *)rsc->getFragment(); - pf->setConstantColor(r, g, b, a); + pf->setConstantColor(rsc, r, g, b, a); } static void SC_uploadToTexture2(RsAllocation va, uint32_t baseMipLevel) diff --git a/libs/rs/rsType.cpp b/libs/rs/rsType.cpp index 79cfd41..478cc95 100644 --- a/libs/rs/rsType.cpp +++ b/libs/rs/rsType.cpp @@ -254,6 +254,20 @@ bool Type::getIsNp2() const return false; } +bool Type::isEqual(const Type *other) const { + if(other == NULL) { + return false; + } + if (other->getElement()->isEqual(getElement()) && + other->getDimX() == mDimX && + other->getDimY() == mDimY && + other->getDimZ() == mDimZ && + other->getDimLOD() == mDimLOD && + other->getDimFaces() == mFaces) { + return true; + } + return false; +} ////////////////////////////////////////////////// // diff --git a/libs/rs/rsType.h b/libs/rs/rsType.h index 5b51e20..33faa87 100644 --- a/libs/rs/rsType.h +++ b/libs/rs/rsType.h @@ -77,6 +77,8 @@ public: virtual RsA3DClassID getClassId() const { return RS_A3D_CLASS_ID_TYPE; } static Type *createFromStream(Context *rsc, IStream *stream); + bool isEqual(const Type *other) const; + protected: struct LOD { size_t mX; diff --git a/libs/utils/ZipFileRO.cpp b/libs/utils/ZipFileRO.cpp index 2d53136..9fcae72 100644 --- a/libs/utils/ZipFileRO.cpp +++ b/libs/utils/ZipFileRO.cpp @@ -22,6 +22,7 @@ #include <utils/ZipFileRO.h> #include <utils/Log.h> #include <utils/misc.h> +#include <utils/threads.h> #include <zlib.h> @@ -195,7 +196,7 @@ bool ZipFileRO::mapCentralDirectory(void) free(scanBuf); return false; } else if (header != kLFHSignature) { - LOGV("Not a Zip archive (found 0x%08x)\n", val); + LOGV("Not a Zip archive (found 0x%08x)\n", header); free(scanBuf); return false; } @@ -496,15 +497,21 @@ bool ZipFileRO::getEntryInfo(ZipEntryRO entry, int* pMethod, size_t* pUncompLen, } unsigned char lfhBuf[kLFHLen]; - if (lseek(mFd, localHdrOffset, SEEK_SET) != localHdrOffset) { - LOGW("failed seeking to lfh at offset %ld\n", localHdrOffset); - return false; - } - ssize_t actual = - TEMP_FAILURE_RETRY(read(mFd, lfhBuf, sizeof(lfhBuf))); - if (actual != sizeof(lfhBuf)) { - LOGW("failed reading lfh from offset %ld\n", localHdrOffset); - return false; + + { + AutoMutex _l(mFdLock); + + if (lseek(mFd, localHdrOffset, SEEK_SET) != localHdrOffset) { + LOGW("failed seeking to lfh at offset %ld\n", localHdrOffset); + return false; + } + + ssize_t actual = + TEMP_FAILURE_RETRY(read(mFd, lfhBuf, sizeof(lfhBuf))); + if (actual != sizeof(lfhBuf)) { + LOGW("failed reading lfh from offset %ld\n", localHdrOffset); + return false; + } } if (get4LE(lfhBuf) != kLFHSignature) { @@ -636,7 +643,7 @@ bool ZipFileRO::uncompressEntry(ZipEntryRO entry, void* buffer) const memcpy(buffer, ptr, uncompLen); } else { if (!inflateBuffer(buffer, ptr, uncompLen, compLen)) - goto bail; + goto unmap; } if (compLen > kSequentialMin) @@ -644,6 +651,8 @@ bool ZipFileRO::uncompressEntry(ZipEntryRO entry, void* buffer) const result = true; +unmap: + file->release(); bail: return result; } @@ -667,7 +676,7 @@ bool ZipFileRO::uncompressEntry(ZipEntryRO entry, int fd) const getEntryInfo(entry, &method, &uncompLen, &compLen, &offset, NULL, NULL); - const FileMap* file = createEntryFileMap(entry); + FileMap* file = createEntryFileMap(entry); if (file == NULL) { goto bail; } @@ -678,21 +687,23 @@ bool ZipFileRO::uncompressEntry(ZipEntryRO entry, int fd) const ssize_t actual = write(fd, ptr, uncompLen); if (actual < 0) { LOGE("Write failed: %s\n", strerror(errno)); - goto bail; + goto unmap; } else if ((size_t) actual != uncompLen) { LOGE("Partial write during uncompress (%zd of %zd)\n", (size_t)actual, (size_t)uncompLen); - goto bail; + goto unmap; } else { LOGI("+++ successful write\n"); } } else { if (!inflateBuffer(fd, ptr, uncompLen, compLen)) - goto bail; + goto unmap; } result = true; +unmap: + file->release(); bail: return result; } diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java index f72eaa4..bc5f9fa 100644 --- a/media/java/android/media/MediaScanner.java +++ b/media/java/android/media/MediaScanner.java @@ -644,9 +644,8 @@ public class MediaScanner } else if (MediaFile.isAudioFileType(mFileType)) { map.put(Audio.Media.ARTIST, (mArtist != null && mArtist.length() > 0) ? mArtist : MediaStore.UNKNOWN_STRING); -// disable album artist support until MediaProvider really supports it -// map.put(Audio.Media.ALBUM_ARTIST, (mAlbumArtist != null && -// mAlbumArtist.length() > 0) ? mAlbumArtist : null); + map.put(Audio.Media.ALBUM_ARTIST, (mAlbumArtist != null && + mAlbumArtist.length() > 0) ? mAlbumArtist : null); map.put(Audio.Media.ALBUM, (mAlbum != null && mAlbum.length() > 0) ? mAlbum : MediaStore.UNKNOWN_STRING); map.put(Audio.Media.COMPOSER, mComposer); diff --git a/media/java/android/media/MtpDatabase.java b/media/java/android/media/MtpDatabase.java index b64299a..403ed58 100644 --- a/media/java/android/media/MtpDatabase.java +++ b/media/java/android/media/MtpDatabase.java @@ -246,8 +246,30 @@ public class MtpDatabase { return new int[] { // allow transfering arbitrary files MtpConstants.FORMAT_UNDEFINED, + MtpConstants.FORMAT_ASSOCIATION, + MtpConstants.FORMAT_TEXT, + MtpConstants.FORMAT_HTML, + MtpConstants.FORMAT_WAV, + MtpConstants.FORMAT_MP3, + MtpConstants.FORMAT_MPEG, + MtpConstants.FORMAT_EXIF_JPEG, + MtpConstants.FORMAT_TIFF_EP, + MtpConstants.FORMAT_GIF, + MtpConstants.FORMAT_JFIF, + MtpConstants.FORMAT_PNG, + MtpConstants.FORMAT_TIFF, + MtpConstants.FORMAT_WMA, + MtpConstants.FORMAT_OGG, + MtpConstants.FORMAT_AAC, + MtpConstants.FORMAT_MP4_CONTAINER, + MtpConstants.FORMAT_MP2, + MtpConstants.FORMAT_3GP_CONTAINER, MtpConstants.FORMAT_ABSTRACT_AV_PLAYLIST, + MtpConstants.FORMAT_WPL_PLAYLIST, + MtpConstants.FORMAT_M3U_PLAYLIST, + MtpConstants.FORMAT_PLS_PLAYLIST, + MtpConstants.FORMAT_XML_DOCUMENT, }; } @@ -260,9 +282,13 @@ public class MtpDatabase { return new int[] { MtpConstants.PROPERTY_STORAGE_ID, MtpConstants.PROPERTY_OBJECT_FORMAT, + MtpConstants.PROPERTY_PROTECTION_STATUS, MtpConstants.PROPERTY_OBJECT_SIZE, MtpConstants.PROPERTY_OBJECT_FILE_NAME, + MtpConstants.PROPERTY_DATE_MODIFIED, MtpConstants.PROPERTY_PARENT_OBJECT, + MtpConstants.PROPERTY_PERSISTENT_UID, + MtpConstants.PROPERTY_NAME, }; } @@ -279,6 +305,11 @@ public class MtpDatabase { String column = null; boolean isString = false; + // temporary hack + if (property == MtpConstants.PROPERTY_NAME) { + property = MtpConstants.PROPERTY_OBJECT_FILE_NAME; + } + switch (property) { case MtpConstants.PROPERTY_STORAGE_ID: outIntValue[0] = mStorageID; diff --git a/media/jni/android_media_MtpDatabase.cpp b/media/jni/android_media_MtpDatabase.cpp index 1842cb2..227ee92 100644 --- a/media/jni/android_media_MtpDatabase.cpp +++ b/media/jni/android_media_MtpDatabase.cpp @@ -672,12 +672,15 @@ struct PropertyTableEntry { }; static const PropertyTableEntry kObjectPropertyTable[] = { - { MTP_PROPERTY_PARENT_OBJECT, MTP_TYPE_UINT32 }, - { MTP_PROPERTY_STORAGE_ID, MTP_TYPE_UINT32 }, - { MTP_PROPERTY_OBJECT_FORMAT, MTP_TYPE_UINT16 }, - { MTP_PROPERTY_OBJECT_FILE_NAME, MTP_TYPE_STR }, - { MTP_PROPERTY_OBJECT_SIZE, MTP_TYPE_UINT64 }, - { MTP_PROPERTY_DATE_MODIFIED, MTP_TYPE_STR }, + { MTP_PROPERTY_STORAGE_ID, MTP_TYPE_UINT32 }, + { MTP_PROPERTY_OBJECT_FORMAT, MTP_TYPE_UINT16 }, + { MTP_PROPERTY_PROTECTION_STATUS, MTP_TYPE_UINT16 }, + { MTP_PROPERTY_OBJECT_SIZE, MTP_TYPE_UINT64 }, + { MTP_PROPERTY_OBJECT_FILE_NAME, MTP_TYPE_STR }, + { MTP_PROPERTY_DATE_MODIFIED, MTP_TYPE_STR }, + { MTP_PROPERTY_PARENT_OBJECT, MTP_TYPE_UINT32 }, + { MTP_PROPERTY_PERSISTENT_UID, MTP_TYPE_UINT128 }, + { MTP_PROPERTY_NAME, MTP_TYPE_STR }, }; static const PropertyTableEntry kDevicePropertyTable[] = { @@ -764,6 +767,7 @@ MtpProperty* MyMtpDatabase::getObjectPropertyDesc(MtpObjectProperty property, case MTP_PROPERTY_PERSISTENT_UID: result = new MtpProperty(property, MTP_TYPE_UINT128); break; + case MTP_PROPERTY_NAME: case MTP_PROPERTY_OBJECT_FILE_NAME: case MTP_PROPERTY_DATE_MODIFIED: result = new MtpProperty(property, MTP_TYPE_STR); @@ -780,6 +784,7 @@ MtpProperty* MyMtpDatabase::getDevicePropertyDesc(MtpDeviceProperty property) { case MTP_DEVICE_PROPERTY_DEVICE_FRIENDLY_NAME: // writeable string properties result = new MtpProperty(property, MTP_TYPE_STR, true); + // FIXME - set current value here! break; } diff --git a/media/jni/audioeffect/android_media_AudioEffect.cpp b/media/jni/audioeffect/android_media_AudioEffect.cpp index b16372d..cb2f0f9 100644 --- a/media/jni/audioeffect/android_media_AudioEffect.cpp +++ b/media/jni/audioeffect/android_media_AudioEffect.cpp @@ -304,14 +304,7 @@ android_media_AudioEffect_native_setup(JNIEnv *env, jobject thiz, jobject weak_t lpJniStorage->mCallbackData.audioEffect_class, &lpJniStorage->mCallbackData); - if (jId) { - nId = (jint *) env->GetPrimitiveArrayCritical(jId, NULL); - if (nId == NULL) { - LOGE("setup: Error retrieving id pointer"); - lStatus = AUDIOEFFECT_ERROR_BAD_VALUE; - goto setup_failure; - } - } else { + if (jId == NULL) { LOGE("setup: NULL java array for id pointer"); lStatus = AUDIOEFFECT_ERROR_BAD_VALUE; goto setup_failure; @@ -336,8 +329,13 @@ android_media_AudioEffect_native_setup(JNIEnv *env, jobject thiz, jobject weak_t goto setup_failure; } + nId = (jint *) env->GetPrimitiveArrayCritical(jId, NULL); + if (nId == NULL) { + LOGE("setup: Error retrieving id pointer"); + lStatus = AUDIOEFFECT_ERROR_BAD_VALUE; + goto setup_failure; + } nId[0] = lpAudioEffect->id(); - env->ReleasePrimitiveArrayCritical(jId, nId, 0); nId = NULL; diff --git a/media/jni/audioeffect/android_media_Visualizer.cpp b/media/jni/audioeffect/android_media_Visualizer.cpp index 7b271ce..57cafd4 100644 --- a/media/jni/audioeffect/android_media_Visualizer.cpp +++ b/media/jni/audioeffect/android_media_Visualizer.cpp @@ -246,14 +246,7 @@ android_media_visualizer_native_setup(JNIEnv *env, jobject thiz, jobject weak_th lpJniStorage->mCallbackData.visualizer_class, &lpJniStorage->mCallbackData); - if (jId) { - nId = (jint *) env->GetPrimitiveArrayCritical(jId, NULL); - if (nId == NULL) { - LOGE("setup: Error retrieving id pointer"); - lStatus = VISUALIZER_ERROR_BAD_VALUE; - goto setup_failure; - } - } else { + if (jId == NULL) { LOGE("setup: NULL java array for id pointer"); lStatus = VISUALIZER_ERROR_BAD_VALUE; goto setup_failure; @@ -275,8 +268,13 @@ android_media_visualizer_native_setup(JNIEnv *env, jobject thiz, jobject weak_th goto setup_failure; } + nId = (jint *) env->GetPrimitiveArrayCritical(jId, NULL); + if (nId == NULL) { + LOGE("setup: Error retrieving id pointer"); + lStatus = VISUALIZER_ERROR_BAD_VALUE; + goto setup_failure; + } nId[0] = lpVisualizer->id(); - env->ReleasePrimitiveArrayCritical(jId, nId, 0); nId = NULL; @@ -424,7 +422,6 @@ android_media_visualizer_native_getWaveForm(JNIEnv *env, jobject thiz, jbyteArra jint status = translateError(lpVisualizer->getWaveForm((uint8_t *)nWaveform)); env->ReleasePrimitiveArrayCritical(jWaveform, nWaveform, 0); - return status; } diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp index f0d8943..ba1e218 100644 --- a/media/libstagefright/MPEG4Writer.cpp +++ b/media/libstagefright/MPEG4Writer.cpp @@ -2111,7 +2111,15 @@ void MPEG4Writer::Track::writeTrackHeader( mOwner->endBox(); // d263 } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime)) { CHECK(mCodecSpecificData); - CHECK(mCodecSpecificDataSize > 0); + CHECK(mCodecSpecificDataSize >= 5); + + // Patch avcc's lengthSize field to match the number + // of bytes we use to indicate the size of a nal unit. + uint8_t *ptr = (uint8_t *)mCodecSpecificData; + ptr[4] = + (ptr[4] & 0xfc) + | (mOwner->useNalLengthFour() ? 3 : 1); + mOwner->beginBox("avcC"); mOwner->write(mCodecSpecificData, mCodecSpecificDataSize); mOwner->endBox(); // avcC diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp index 7a29bd2..4d69dd3 100644 --- a/media/libstagefright/OMXCodec.cpp +++ b/media/libstagefright/OMXCodec.cpp @@ -205,12 +205,14 @@ static const CodecInfo kEncoderInfo[] = { { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.qcom.7x30.video.encoder.mpeg4" }, { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.qcom.video.encoder.mpeg4" }, { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.TI.Video.encoder" }, + { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.Nvidia.mp4.encoder" }, { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.SEC.MPEG4.Encoder" }, { MEDIA_MIMETYPE_VIDEO_MPEG4, "M4vH263Encoder" }, // { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.PV.mpeg4enc" }, { MEDIA_MIMETYPE_VIDEO_H263, "OMX.qcom.7x30.video.encoder.h263" }, { MEDIA_MIMETYPE_VIDEO_H263, "OMX.qcom.video.encoder.h263" }, { MEDIA_MIMETYPE_VIDEO_H263, "OMX.TI.Video.encoder" }, + { MEDIA_MIMETYPE_VIDEO_H263, "OMX.Nvidia.h263.encoder" }, { MEDIA_MIMETYPE_VIDEO_H263, "OMX.SEC.H263.Encoder" }, { MEDIA_MIMETYPE_VIDEO_H263, "M4vH263Encoder" }, // { MEDIA_MIMETYPE_VIDEO_H263, "OMX.PV.h263enc" }, diff --git a/media/mtp/MtpCursor.cpp b/media/mtp/MtpCursor.cpp index 8c964b4..865a294 100644 --- a/media/mtp/MtpCursor.cpp +++ b/media/mtp/MtpCursor.cpp @@ -66,7 +66,8 @@ namespace android { #define OBJECT_THUMB 221 MtpCursor::MtpCursor(MtpClient* client, int queryType, int deviceID, - int storageID, int objectID, int columnCount, int* columns) + MtpStorageID storageID, MtpObjectHandle objectID, + int columnCount, int* columns) : mClient(client), mQueryType(queryType), mDeviceID(deviceID), @@ -427,7 +428,8 @@ bool MtpCursor::putString(CursorWindow* window, const char* text, int row, int c return true; } -bool MtpCursor::putThumbnail(CursorWindow* window, int objectID, int format, int row, int column) { +bool MtpCursor::putThumbnail(CursorWindow* window, MtpObjectHandle objectID, + MtpObjectFormat format, int row, int column) { MtpDevice* device = mClient->getDevice(mDeviceID); void* thumbnail; int size, offset; diff --git a/media/mtp/MtpCursor.h b/media/mtp/MtpCursor.h index 3f84753..9e9833f 100644 --- a/media/mtp/MtpCursor.h +++ b/media/mtp/MtpCursor.h @@ -36,17 +36,18 @@ private: OBJECT_CHILDREN = 8, }; - MtpClient* mClient; - int mQueryType; - int mDeviceID; - int mStorageID; - int mQbjectID; - int mColumnCount; - int* mColumns; + MtpClient* mClient; + int mQueryType; + int mDeviceID; + MtpStorageID mStorageID; + MtpObjectHandle mQbjectID; + int mColumnCount; + int* mColumns; public: MtpCursor(MtpClient* client, int queryType, int deviceID, - int storageID, int objectID, int columnCount, int* columns); + MtpStorageID storageID, MtpObjectHandle objectID, + int columnCount, int* columns); virtual ~MtpCursor(); int fillWindow(CursorWindow* window, int startPos); @@ -68,7 +69,8 @@ private: bool prepareRow(CursorWindow* window); bool putLong(CursorWindow* window, int value, int row, int column); bool putString(CursorWindow* window, const char* text, int row, int column); - bool putThumbnail(CursorWindow* window, int objectID, int format, int row, int column); + bool putThumbnail(CursorWindow* window, MtpObjectHandle objectID, + MtpObjectFormat format, int row, int column); }; }; // namespace android diff --git a/media/mtp/MtpServer.cpp b/media/mtp/MtpServer.cpp index 6332b4e..84a3e2c 100644 --- a/media/mtp/MtpServer.cpp +++ b/media/mtp/MtpServer.cpp @@ -26,6 +26,8 @@ #include <cutils/properties.h> +#define LOG_TAG "MtpServer" + #include "MtpDebug.h" #include "MtpDatabase.h" #include "MtpProperty.h" @@ -68,8 +70,8 @@ static const MtpOperationCode kSupportedOperationCodes[] = { // MTP_OPERATION_INITIATE_OPEN_CAPTURE, MTP_OPERATION_GET_OBJECT_PROPS_SUPPORTED, MTP_OPERATION_GET_OBJECT_PROP_DESC, -// MTP_OPERATION_GET_OBJECT_PROP_VALUE, -// MTP_OPERATION_SET_OBJECT_PROP_VALUE, + MTP_OPERATION_GET_OBJECT_PROP_VALUE, + MTP_OPERATION_SET_OBJECT_PROP_VALUE, MTP_OPERATION_GET_OBJECT_REFERENCES, MTP_OPERATION_SET_OBJECT_REFERENCES, // MTP_OPERATION_SKIP, @@ -294,6 +296,7 @@ bool MtpServer::handleRequest() { response = doGetDevicePropDesc(); break; default: + LOGE("got unsupported command %s", MtpDebug::getOperationCodeName(operation)); response = MTP_RESPONSE_OPERATION_NOT_SUPPORTED; break; } diff --git a/media/tests/CameraBrowser/src/com/android/camerabrowser/ObjectViewer.java b/media/tests/CameraBrowser/src/com/android/camerabrowser/ObjectViewer.java index 5899bc1..9f2f98e 100644 --- a/media/tests/CameraBrowser/src/com/android/camerabrowser/ObjectViewer.java +++ b/media/tests/CameraBrowser/src/com/android/camerabrowser/ObjectViewer.java @@ -200,9 +200,8 @@ public class ObjectViewer extends Activity { } // temporary workaround until we straighten out permissions in /data/media - // 1015 is AID_SDCARD_RW - FileUtils.setPermissions(destDir.getPath(), 0775, Process.myUid(), 1015); - FileUtils.setPermissions(destFile.getPath(), 0664, Process.myUid(), 1015); + FileUtils.setPermissions(destDir.getPath(), 0775, Process.myUid(), Process.SDCARD_RW_GID); + FileUtils.setPermissions(destFile.getPath(), 0664, Process.myUid(), Process.SDCARD_RW_GID); success = true; } diff --git a/opengl/libs/EGL/egl.cpp b/opengl/libs/EGL/egl.cpp index d99fc1e..bc944a0 100644 --- a/opengl/libs/EGL/egl.cpp +++ b/opengl/libs/EGL/egl.cpp @@ -444,19 +444,14 @@ static void(*findProcAddress(const char* name, // ---------------------------------------------------------------------------- -static void gl_no_context() { +static int gl_no_context() { tls_t* tls = getTLS(); if (tls->logCallWithNoContext == EGL_TRUE) { tls->logCallWithNoContext = EGL_FALSE; LOGE("call to OpenGL ES API with no current context " "(logged once per thread)"); } -} - -// Always return GL_INVALID_OPERATION from glGetError() when called from -// a thread without a bound context. -static GLenum gl_no_context_glGetError() { - return GL_INVALID_OPERATION; + return 0; } static void early_egl_init(void) @@ -470,8 +465,6 @@ static void early_egl_init(void) addr, sizeof(gHooksNoContext)); - gHooksNoContext.gl.glGetError = gl_no_context_glGetError; - setGlThreadSpecific(&gHooksNoContext); } diff --git a/opengl/libs/GLES2/gl2.cpp b/opengl/libs/GLES2/gl2.cpp index a12edf2..fee4609 100644 --- a/opengl/libs/GLES2/gl2.cpp +++ b/opengl/libs/GLES2/gl2.cpp @@ -60,6 +60,7 @@ using namespace android; "ldr r12, [r12, %[tls]] \n" \ "cmp r12, #0 \n" \ "ldrne pc, [r12, %[api]] \n" \ + "mov r0, #0 \n" \ "bx lr \n" \ : \ : [tls] "J"(TLS_SLOT_OPENGL_API*4), \ diff --git a/opengl/libs/GLES_CM/gl.cpp b/opengl/libs/GLES_CM/gl.cpp index d71ff76..ee29f12 100644 --- a/opengl/libs/GLES_CM/gl.cpp +++ b/opengl/libs/GLES_CM/gl.cpp @@ -114,6 +114,7 @@ GL_API void GL_APIENTRY glWeightPointerOESBounds(GLint size, GLenum type, "ldr r12, [r12, %[tls]] \n" \ "cmp r12, #0 \n" \ "ldrne pc, [r12, %[api]] \n" \ + "mov r0, #0 \n" \ "bx lr \n" \ : \ : [tls] "J"(TLS_SLOT_OPENGL_API*4), \ diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java index ade93da..6e2bfdb 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java @@ -327,10 +327,7 @@ public class SettingsProvider extends ContentProvider { try { final String value = c.moveToNext() ? c.getString(0) : null; if (value == null) { - final SecureRandom random = SecureRandom.getInstance("SHA1PRNG"); - String serial = SystemProperties.get("ro.serialno", ""); - random.setSeed( - (serial + System.nanoTime() + new SecureRandom().nextLong()).getBytes()); + final SecureRandom random = new SecureRandom(); final String newAndroidIdValue = Long.toHexString(random.nextLong()); Log.d(TAG, "Generated and saved new ANDROID_ID [" + newAndroidIdValue + "]"); final ContentValues values = new ContentValues(); @@ -342,8 +339,6 @@ public class SettingsProvider extends ContentProvider { } } return true; - } catch (NoSuchAlgorithmException e) { - return false; } finally { c.close(); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java index d98bd7d..0ca0572 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java @@ -22,6 +22,7 @@ import android.content.res.Resources; import android.graphics.drawable.Drawable; import android.graphics.Canvas; import android.util.Slog; +import android.util.Log; import android.view.ViewDebug; import android.widget.FrameLayout; @@ -124,4 +125,10 @@ public class StatusBarIconView extends AnimatedImageView { public StatusBarIcon getStatusBarIcon() { return mIcon; } + + protected void debug(int depth) { + super.debug(depth); + Log.d("View", debugIndent(depth) + "slot=" + mSlot); + Log.d("View", debugIndent(depth) + "icon=" + mIcon); + } } diff --git a/policy/src/com/android/internal/policy/impl/LockScreen.java b/policy/src/com/android/internal/policy/impl/LockScreen.java index f8c0aba..3583ab9 100644 --- a/policy/src/com/android/internal/policy/impl/LockScreen.java +++ b/policy/src/com/android/internal/policy/impl/LockScreen.java @@ -20,6 +20,8 @@ import com.android.internal.R; import com.android.internal.telephony.IccCard; import com.android.internal.widget.LockPatternUtils; import com.android.internal.widget.SlidingTab; +import com.android.internal.widget.WaveView; +import com.android.internal.widget.WaveView.OnTriggerListener; import android.content.Context; import android.content.res.Configuration; @@ -46,8 +48,9 @@ import java.io.File; * information about the device depending on its state, and how to get * past it, as applicable. */ -class LockScreen extends LinearLayout implements KeyguardScreen, KeyguardUpdateMonitor.InfoCallback, - KeyguardUpdateMonitor.SimStateCallback, SlidingTab.OnTriggerListener { +class LockScreen extends LinearLayout implements KeyguardScreen, + KeyguardUpdateMonitor.InfoCallback, + KeyguardUpdateMonitor.SimStateCallback { private static final boolean DBG = false; private static final String TAG = "LockScreen"; @@ -59,7 +62,7 @@ class LockScreen extends LinearLayout implements KeyguardScreen, KeyguardUpdateM private final KeyguardUpdateMonitor mUpdateMonitor; private final KeyguardScreenCallback mCallback; - private SlidingTab mSelector; + private SlidingTab mSlidingTab; private TextView mScreenLocked; private TextView mEmergencyCallText; private Button mEmergencyCallButton; @@ -89,6 +92,9 @@ class LockScreen extends LinearLayout implements KeyguardScreen, KeyguardUpdateM private boolean mEnableMenuKeyInLockScreen; private StatusView mStatusView; + private WaveView mEnergyWave; + private SlidingTabMethods mSlidingTabMethods; + private WaveViewMethods mWaveViewMethods; /** * The status of this lock screen. @@ -141,6 +147,91 @@ class LockScreen extends LinearLayout implements KeyguardScreen, KeyguardUpdateM } } + class SlidingTabMethods implements SlidingTab.OnTriggerListener { + + private void updateRightTabResources() { + boolean vibe = mSilentMode + && (mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_VIBRATE); + + mSlidingTab.setRightTabResources( + mSilentMode ? ( vibe ? R.drawable.ic_jog_dial_vibrate_on + : R.drawable.ic_jog_dial_sound_off ) + : R.drawable.ic_jog_dial_sound_on, + mSilentMode ? R.drawable.jog_tab_target_yellow + : R.drawable.jog_tab_target_gray, + mSilentMode ? R.drawable.jog_tab_bar_right_sound_on + : R.drawable.jog_tab_bar_right_sound_off, + mSilentMode ? R.drawable.jog_tab_right_sound_on + : R.drawable.jog_tab_right_sound_off); + } + + /** {@inheritDoc} */ + public void onTrigger(View v, int whichHandle) { + if (whichHandle == SlidingTab.OnTriggerListener.LEFT_HANDLE) { + mCallback.goToUnlockScreen(); + } else if (whichHandle == SlidingTab.OnTriggerListener.RIGHT_HANDLE) { + // toggle silent mode + mSilentMode = !mSilentMode; + if (mSilentMode) { + final boolean vibe = (Settings.System.getInt( + getContext().getContentResolver(), + Settings.System.VIBRATE_IN_SILENT, 1) == 1); + + mAudioManager.setRingerMode(vibe + ? AudioManager.RINGER_MODE_VIBRATE + : AudioManager.RINGER_MODE_SILENT); + } else { + mAudioManager.setRingerMode(AudioManager.RINGER_MODE_NORMAL); + } + + updateRightTabResources(); + + String message = mSilentMode ? + getContext().getString(R.string.global_action_silent_mode_on_status) : + getContext().getString(R.string.global_action_silent_mode_off_status); + + final int toastIcon = mSilentMode + ? R.drawable.ic_lock_ringer_off + : R.drawable.ic_lock_ringer_on; + + final int toastColor = mSilentMode + ? getContext().getResources().getColor(R.color.keyguard_text_color_soundoff) + : getContext().getResources().getColor(R.color.keyguard_text_color_soundon); + toastMessage(mScreenLocked, message, toastColor, toastIcon); + mCallback.pokeWakelock(); + } + } + + /** {@inheritDoc} */ + public void onGrabbedStateChange(View v, int grabbedState) { + if (grabbedState == SlidingTab.OnTriggerListener.RIGHT_HANDLE) { + mSilentMode = isSilentMode(); + mSlidingTab.setRightHintText(mSilentMode ? R.string.lockscreen_sound_on_label + : R.string.lockscreen_sound_off_label); + } + mCallback.pokeWakelock(); + } + } + + class WaveViewMethods implements WaveView.OnTriggerListener { + /** {@inheritDoc} */ + public void onTrigger(View v, int whichHandle) { + if (whichHandle == WaveView.OnTriggerListener.CENTER_HANDLE) { + // Delay hiding lock screen long enough for animation to finish + postDelayed(new Runnable() { + public void run() { + mCallback.goToUnlockScreen(); + } + }, 500); + } + } + + /** {@inheritDoc} */ + public void onGrabbedStateChange(View v, int grabbedState) { + mCallback.pokeWakelock(); + } + } + /** * In general, we enable unlocking the insecure key guard with the menu key. However, there are * some cases where we wish to disable it, notably when the menu button placement or technology @@ -195,9 +286,6 @@ class LockScreen extends LinearLayout implements KeyguardScreen, KeyguardUpdateM mStatusView = new StatusView(this, mUpdateMonitor, mLockPatternUtils); mScreenLocked = (TextView) findViewById(R.id.screenLocked); - mSelector = (SlidingTab) findViewById(R.id.tab_selector); - mSelector.setHoldAfterTrigger(true, false); - mSelector.setLeftHintText(R.string.lockscreen_unlock_label); mEmergencyCallText = (TextView) findViewById(R.id.emergencyCallText); mEmergencyCallButton = (Button) findViewById(R.id.emergencyCallButton); @@ -220,15 +308,25 @@ class LockScreen extends LinearLayout implements KeyguardScreen, KeyguardUpdateM mAudioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE); mSilentMode = isSilentMode(); - mSelector.setLeftTabResources( - R.drawable.ic_jog_dial_unlock, - R.drawable.jog_tab_target_green, - R.drawable.jog_tab_bar_left_unlock, - R.drawable.jog_tab_left_unlock); - - updateRightTabResources(); - - mSelector.setOnTriggerListener(this); + mSlidingTab = (SlidingTab) findViewById(R.id.tab_selector); + mEnergyWave = (WaveView) findViewById(R.id.wave_view); + if (mSlidingTab != null) { + mSlidingTab.setHoldAfterTrigger(true, false); + mSlidingTab.setLeftHintText(R.string.lockscreen_unlock_label); + mSlidingTab.setLeftTabResources( + R.drawable.ic_jog_dial_unlock, + R.drawable.jog_tab_target_green, + R.drawable.jog_tab_bar_left_unlock, + R.drawable.jog_tab_left_unlock); + mSlidingTabMethods = new SlidingTabMethods(); + mSlidingTab.setOnTriggerListener(mSlidingTabMethods); + mSlidingTabMethods.updateRightTabResources(); + } else if (mEnergyWave != null) { + mWaveViewMethods = new WaveViewMethods(); + mEnergyWave.setOnTriggerListener(mWaveViewMethods); + } else { + throw new IllegalStateException("Must have either SlidingTab or WaveView defined"); + } resetStatusInfo(updateMonitor); } @@ -237,22 +335,6 @@ class LockScreen extends LinearLayout implements KeyguardScreen, KeyguardUpdateM return mAudioManager.getRingerMode() != AudioManager.RINGER_MODE_NORMAL; } - private void updateRightTabResources() { - boolean vibe = mSilentMode - && (mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_VIBRATE); - - mSelector.setRightTabResources( - mSilentMode ? ( vibe ? R.drawable.ic_jog_dial_vibrate_on - : R.drawable.ic_jog_dial_sound_off ) - : R.drawable.ic_jog_dial_sound_on, - mSilentMode ? R.drawable.jog_tab_target_yellow - : R.drawable.jog_tab_target_gray, - mSilentMode ? R.drawable.jog_tab_bar_right_sound_on - : R.drawable.jog_tab_bar_right_sound_off, - mSilentMode ? R.drawable.jog_tab_right_sound_on - : R.drawable.jog_tab_right_sound_off); - } - private void resetStatusInfo(KeyguardUpdateMonitor updateMonitor) { mShowingBatteryInfo = updateMonitor.shouldShowBatteryInfo(); mPluggedIn = updateMonitor.isDevicePluggedIn(); @@ -278,53 +360,6 @@ class LockScreen extends LinearLayout implements KeyguardScreen, KeyguardUpdateM return false; } - /** {@inheritDoc} */ - public void onTrigger(View v, int whichHandle) { - if (whichHandle == SlidingTab.OnTriggerListener.LEFT_HANDLE) { - mCallback.goToUnlockScreen(); - } else if (whichHandle == SlidingTab.OnTriggerListener.RIGHT_HANDLE) { - // toggle silent mode - mSilentMode = !mSilentMode; - if (mSilentMode) { - final boolean vibe = (Settings.System.getInt( - getContext().getContentResolver(), - Settings.System.VIBRATE_IN_SILENT, 1) == 1); - - mAudioManager.setRingerMode(vibe - ? AudioManager.RINGER_MODE_VIBRATE - : AudioManager.RINGER_MODE_SILENT); - } else { - mAudioManager.setRingerMode(AudioManager.RINGER_MODE_NORMAL); - } - - updateRightTabResources(); - - String message = mSilentMode ? - getContext().getString(R.string.global_action_silent_mode_on_status) : - getContext().getString(R.string.global_action_silent_mode_off_status); - - final int toastIcon = mSilentMode - ? R.drawable.ic_lock_ringer_off - : R.drawable.ic_lock_ringer_on; - - final int toastColor = mSilentMode - ? getContext().getResources().getColor(R.color.keyguard_text_color_soundoff) - : getContext().getResources().getColor(R.color.keyguard_text_color_soundon); - toastMessage(mScreenLocked, message, toastColor, toastIcon); - mCallback.pokeWakelock(); - } - } - - /** {@inheritDoc} */ - public void onGrabbedStateChange(View v, int grabbedState) { - if (grabbedState == SlidingTab.OnTriggerListener.RIGHT_HANDLE) { - mSilentMode = isSilentMode(); - mSelector.setRightHintText(mSilentMode ? R.string.lockscreen_sound_on_label - : R.string.lockscreen_sound_off_label); - } - mCallback.pokeWakelock(); - } - /** * Displays a message in a text view and then restores the previous text. * @param textView The text view. @@ -460,6 +495,22 @@ class LockScreen extends LinearLayout implements KeyguardScreen, KeyguardUpdateM } /** + * Enables unlocking of this screen. Typically just shows the unlock widget. + */ + private void enableUnlock() { + if (mEnergyWave != null) mEnergyWave.setVisibility(View.VISIBLE); + if (mSlidingTab != null) mSlidingTab.setVisibility(View.VISIBLE); + } + + /** + * Disable unlocking of this screen. Typically just hides the unlock widget. + */ + private void disableUnlock() { + if (mEnergyWave != null) mEnergyWave.setVisibility(View.GONE); + if (mSlidingTab != null) mSlidingTab.setVisibility(View.GONE); + } + + /** * Update the layout to match the current status. */ private void updateLayout(Status status) { @@ -481,9 +532,10 @@ class LockScreen extends LinearLayout implements KeyguardScreen, KeyguardUpdateM // layout mScreenLocked.setVisibility(View.INVISIBLE); - mSelector.setVisibility(View.VISIBLE); mEmergencyCallText.setVisibility(View.GONE); + enableUnlock(); break; + case NetworkLocked: // The carrier string shows both sim card status (i.e. No Sim Card) and // carrier's name and/or "Emergency Calls Only" status @@ -495,9 +547,10 @@ class LockScreen extends LinearLayout implements KeyguardScreen, KeyguardUpdateM // layout mScreenLocked.setVisibility(View.VISIBLE); - mSelector.setVisibility(View.VISIBLE); mEmergencyCallText.setVisibility(View.GONE); + enableUnlock(); break; + case SimMissing: // text mStatusView.setCarrierText(R.string.lockscreen_missing_sim_message_short); @@ -505,10 +558,10 @@ class LockScreen extends LinearLayout implements KeyguardScreen, KeyguardUpdateM // layout mScreenLocked.setVisibility(View.VISIBLE); - mSelector.setVisibility(View.VISIBLE); mEmergencyCallText.setVisibility(View.VISIBLE); - // do not need to show the e-call button; user may unlock + enableUnlock(); // do not need to show the e-call button; user may unlock break; + case SimMissingLocked: // text mStatusView.setCarrierText( @@ -519,10 +572,11 @@ class LockScreen extends LinearLayout implements KeyguardScreen, KeyguardUpdateM // layout mScreenLocked.setVisibility(View.VISIBLE); - mSelector.setVisibility(View.GONE); // cannot unlock mEmergencyCallText.setVisibility(View.VISIBLE); mEmergencyCallButton.setVisibility(View.VISIBLE); + disableUnlock(); break; + case SimLocked: // text mStatusView.setCarrierText( @@ -532,9 +586,10 @@ class LockScreen extends LinearLayout implements KeyguardScreen, KeyguardUpdateM // layout mScreenLocked.setVisibility(View.INVISIBLE); - mSelector.setVisibility(View.VISIBLE); mEmergencyCallText.setVisibility(View.GONE); + enableUnlock(); break; + case SimPukLocked: // text mStatusView.setCarrierText( @@ -545,9 +600,9 @@ class LockScreen extends LinearLayout implements KeyguardScreen, KeyguardUpdateM // layout mScreenLocked.setVisibility(View.VISIBLE); - mSelector.setVisibility(View.GONE); // cannot unlock mEmergencyCallText.setVisibility(View.VISIBLE); mEmergencyCallButton.setVisibility(View.VISIBLE); + disableUnlock(); break; } } @@ -614,7 +669,9 @@ class LockScreen extends LinearLayout implements KeyguardScreen, KeyguardUpdateM /** {@inheritDoc} */ public void onPause() { - + if (mEnergyWave != null) { + mEnergyWave.reset(); + } } /** {@inheritDoc} */ @@ -632,7 +689,7 @@ class LockScreen extends LinearLayout implements KeyguardScreen, KeyguardUpdateM boolean silent = AudioManager.RINGER_MODE_NORMAL != state; if (silent != mSilentMode) { mSilentMode = silent; - updateRightTabResources(); + if (mSlidingTabMethods != null) mSlidingTabMethods.updateRightTabResources(); } } diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java index 7009c65..c047522 100755 --- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java +++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java @@ -235,8 +235,11 @@ public class PhoneWindowManager implements WindowManagerPolicy { @Override public void handleMotion(MotionEvent event, Runnable finishedCallback) { finishedCallback.run(); + synchronized (mLock) { - mPointerLocationView.addTouchEvent(event); + if (mPointerLocationView != null) { + mPointerLocationView.addTouchEvent(event); + } } } }; @@ -287,7 +290,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { // (See Settings.Secure.INCALL_POWER_BUTTON_BEHAVIOR.) int mIncallPowerBehavior; - int mLandscapeRotation = -1; + int mLandscapeRotation = -1; // default landscape rotation + int mSeascapeRotation = -1; // "other" landscape rotation, 180 degrees from mLandscapeRotation int mPortraitRotation = -1; // Nothing to see here, move along... @@ -360,9 +364,12 @@ public class PhoneWindowManager implements WindowManagerPolicy { return true; } // The user preference says we can rotate, and the app is willing to rotate. + // Note we include SCREEN_ORIENTATION_LANDSCAPE since we can use the sensor to choose + // between the two possible landscape rotations. if (mAccelerometerDefault != 0 && (appOrientation == ActivityInfo.SCREEN_ORIENTATION_USER - || appOrientation == ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED)) { + || appOrientation == ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED + || appOrientation == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE)) { return true; } // We're in a dock that has a rotation affinity, an the app is willing to rotate. @@ -371,7 +378,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { // Note we override the nosensor flag here. if (appOrientation == ActivityInfo.SCREEN_ORIENTATION_USER || appOrientation == ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED - || appOrientation == ActivityInfo.SCREEN_ORIENTATION_NOSENSOR) { + || appOrientation == ActivityInfo.SCREEN_ORIENTATION_NOSENSOR + || appOrientation == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) { return true; } } @@ -2117,20 +2125,20 @@ public class PhoneWindowManager implements WindowManagerPolicy { if (d.getWidth() > d.getHeight()) { mPortraitRotation = Surface.ROTATION_90; mLandscapeRotation = Surface.ROTATION_0; + mSeascapeRotation = Surface.ROTATION_180; } else { mPortraitRotation = Surface.ROTATION_0; mLandscapeRotation = Surface.ROTATION_90; + mSeascapeRotation = Surface.ROTATION_270; } } synchronized (mLock) { - switch (orientation) { - case ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE: - //always return landscape if orientation set to landscape - return mLandscapeRotation; - case ActivityInfo.SCREEN_ORIENTATION_PORTRAIT: - //always return portrait if orientation set to portrait - return mPortraitRotation; + if (orientation == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT) { + //always return portrait if orientation set to portrait + return mPortraitRotation; + } else if (orientation == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) { + return getCurrentLandscapeRotation(lastRotation); } // case for nosensor meaning ignore sensor and consider only lid // or orientation sensor disabled @@ -2150,6 +2158,26 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } + private int getCurrentLandscapeRotation(int lastRotation) { + // landscape-only apps can take either landscape rotation + if (useSensorForOrientationLp(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE)) { + int sensorRotation = mOrientationListener.getCurrentRotation(lastRotation); + if (isLandscapeOrSeascape(sensorRotation)) { + return sensorRotation; + } + } + // try to preserve the old rotation if it was landscape + if (isLandscapeOrSeascape(lastRotation)) { + return lastRotation; + } + // default to one of the two landscape rotations + return mLandscapeRotation; + } + + private boolean isLandscapeOrSeascape(int sensorRotation) { + return sensorRotation == mLandscapeRotation || sensorRotation == mSeascapeRotation; + } + public boolean detectSafeMode() { try { int menuState = mWindowManager.getKeycodeState(KeyEvent.KEYCODE_MENU); diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp index 56de765..8a732ed 100644 --- a/services/audioflinger/AudioFlinger.cpp +++ b/services/audioflinger/AudioFlinger.cpp @@ -4653,29 +4653,44 @@ sp<IEffect> AudioFlinger::createEffect(pid_t pid, goto Exit; } - { - Mutex::Autolock _l(mLock); + // check audio settings permission for global effects + if (sessionId == AudioSystem::SESSION_OUTPUT_MIX && !settingsAllowed()) { + lStatus = PERMISSION_DENIED; + goto Exit; + } - // check audio settings permission for global effects - if (sessionId == AudioSystem::SESSION_OUTPUT_MIX && !settingsAllowed()) { - lStatus = PERMISSION_DENIED; - goto Exit; - } + // Session AudioSystem::SESSION_OUTPUT_STAGE is reserved for output stage effects + // that can only be created by audio policy manager (running in same process) + if (sessionId == AudioSystem::SESSION_OUTPUT_STAGE && getpid() != pid) { + lStatus = PERMISSION_DENIED; + goto Exit; + } - // Session AudioSystem::SESSION_OUTPUT_STAGE is reserved for output stage effects - // that can only be created by audio policy manager (running in same process) - if (sessionId == AudioSystem::SESSION_OUTPUT_STAGE && getpid() != pid) { - lStatus = PERMISSION_DENIED; - goto Exit; - } + // check recording permission for visualizer + if ((memcmp(&pDesc->type, SL_IID_VISUALIZATION, sizeof(effect_uuid_t)) == 0 || + memcmp(&pDesc->uuid, &VISUALIZATION_UUID_, sizeof(effect_uuid_t)) == 0) && + !recordingAllowed()) { + lStatus = PERMISSION_DENIED; + goto Exit; + } - // check recording permission for visualizer - if ((memcmp(&pDesc->type, SL_IID_VISUALIZATION, sizeof(effect_uuid_t)) == 0 || - memcmp(&pDesc->uuid, &VISUALIZATION_UUID_, sizeof(effect_uuid_t)) == 0) && - !recordingAllowed()) { - lStatus = PERMISSION_DENIED; + if (output == 0) { + if (sessionId == AudioSystem::SESSION_OUTPUT_STAGE) { + // output must be specified by AudioPolicyManager when using session + // AudioSystem::SESSION_OUTPUT_STAGE + lStatus = BAD_VALUE; goto Exit; + } else if (sessionId == AudioSystem::SESSION_OUTPUT_MIX) { + // if the output returned by getOutputForEffect() is removed before we lock the + // mutex below, the call to checkPlaybackThread_l(output) below will detect it + // and we will exit safely + output = AudioSystem::getOutputForEffect(&desc); } + } + + { + Mutex::Autolock _l(mLock); + if (!EffectIsNullUuid(&pDesc->uuid)) { // if uuid is specified, request effect descriptor @@ -4744,32 +4759,24 @@ sp<IEffect> AudioFlinger::createEffect(pid_t pid, // If output is not specified try to find a matching audio session ID in one of the // output threads. - // TODO: allow attachment of effect to inputs + // If output is 0 here, sessionId is neither SESSION_OUTPUT_STAGE nor SESSION_OUTPUT_MIX + // because of code checking output when entering the function. if (output == 0) { - if (sessionId == AudioSystem::SESSION_OUTPUT_STAGE) { - // output must be specified by AudioPolicyManager when using session - // AudioSystem::SESSION_OUTPUT_STAGE - lStatus = BAD_VALUE; - goto Exit; - } else if (sessionId == AudioSystem::SESSION_OUTPUT_MIX) { - output = AudioSystem::getOutputForEffect(&desc); - LOGV("createEffect() got output %d for effect %s", output, desc.name); - } else { - // look for the thread where the specified audio session is present - for (size_t i = 0; i < mPlaybackThreads.size(); i++) { - if (mPlaybackThreads.valueAt(i)->hasAudioSession(sessionId) != 0) { - output = mPlaybackThreads.keyAt(i); - break; - } - } - // If no output thread contains the requested session ID, default to - // first output. The effect chain will be moved to the correct output - // thread when a track with the same session ID is created - if (output == 0 && mPlaybackThreads.size()) { - output = mPlaybackThreads.keyAt(0); + // look for the thread where the specified audio session is present + for (size_t i = 0; i < mPlaybackThreads.size(); i++) { + if (mPlaybackThreads.valueAt(i)->hasAudioSession(sessionId) != 0) { + output = mPlaybackThreads.keyAt(i); + break; } } + // If no output thread contains the requested session ID, default to + // first output. The effect chain will be moved to the correct output + // thread when a track with the same session ID is created + if (output == 0 && mPlaybackThreads.size()) { + output = mPlaybackThreads.keyAt(0); + } } + LOGV("createEffect() got output %d for effect %s", output, desc.name); PlaybackThread *thread = checkPlaybackThread_l(output); if (thread == NULL) { LOGE("createEffect() unknown output thread"); @@ -4777,6 +4784,8 @@ sp<IEffect> AudioFlinger::createEffect(pid_t pid, goto Exit; } + // TODO: allow attachment of effect to inputs + wclient = mClients.valueFor(pid); if (wclient != NULL) { diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java index aae3cff..6095117 100644 --- a/services/java/com/android/server/ConnectivityService.java +++ b/services/java/com/android/server/ConnectivityService.java @@ -114,6 +114,71 @@ public class ConnectivityService extends IConnectivityManager.Stub { private boolean mTestMode; private static ConnectivityService sServiceInstance; + private static final int ENABLED = 1; + private static final int DISABLED = 0; + + // Share the event space with NetworkStateTracker (which can't see this + // internal class but sends us events). If you change these, change + // NetworkStateTracker.java too. + private static final int MIN_NETWORK_STATE_TRACKER_EVENT = 1; + private static final int MAX_NETWORK_STATE_TRACKER_EVENT = 100; + + /** + * used internally as a delayed event to make us switch back to the + * default network + */ + private static final int EVENT_RESTORE_DEFAULT_NETWORK = + MAX_NETWORK_STATE_TRACKER_EVENT + 1; + + /** + * used internally to change our mobile data enabled flag + */ + private static final int EVENT_CHANGE_MOBILE_DATA_ENABLED = + MAX_NETWORK_STATE_TRACKER_EVENT + 2; + + /** + * used internally to change our network preference setting + * arg1 = networkType to prefer + */ + private static final int EVENT_SET_NETWORK_PREFERENCE = + MAX_NETWORK_STATE_TRACKER_EVENT + 3; + + /** + * used internally to synchronize inet condition reports + * arg1 = networkType + * arg2 = condition (0 bad, 100 good) + */ + private static final int EVENT_INET_CONDITION_CHANGE = + MAX_NETWORK_STATE_TRACKER_EVENT + 4; + + /** + * used internally to mark the end of inet condition hold periods + * arg1 = networkType + */ + private static final int EVENT_INET_CONDITION_HOLD_END = + MAX_NETWORK_STATE_TRACKER_EVENT + 5; + + /** + * used internally to set the background data preference + * arg1 = TRUE for enabled, FALSE for disabled + */ + private static final int EVENT_SET_BACKGROUND_DATA = + MAX_NETWORK_STATE_TRACKER_EVENT + 6; + + /** + * used internally to set enable/disable cellular data + * arg1 = ENBALED or DISABLED + */ + private static final int EVENT_SET_MOBILE_DATA = + MAX_NETWORK_STATE_TRACKER_EVENT + 7; + + /** + * used internally to clear a wakelock when transitioning + * from one net to another + */ + private static final int EVENT_CLEAR_NET_TRANSITION_WAKELOCK = + MAX_NETWORK_STATE_TRACKER_EVENT + 8; + private Handler mHandler; // list of DeathRecipients used to make sure features are turned off when @@ -354,30 +419,36 @@ public class ConnectivityService extends IConnectivityManager.Stub { * Sets the preferred network. * @param preference the new preference */ - public synchronized void setNetworkPreference(int preference) { + public void setNetworkPreference(int preference) { enforceChangePermission(); + + mHandler.sendMessage(mHandler.obtainMessage(EVENT_SET_NETWORK_PREFERENCE, preference, 0)); + } + + public int getNetworkPreference() { + enforceAccessPermission(); + int preference; + synchronized(this) { + preference = mNetworkPreference; + } + return preference; + } + + private void handleSetNetworkPreference(int preference) { if (ConnectivityManager.isNetworkTypeValid(preference) && mNetAttributes[preference] != null && mNetAttributes[preference].isDefault()) { if (mNetworkPreference != preference) { - persistNetworkPreference(preference); - mNetworkPreference = preference; + final ContentResolver cr = mContext.getContentResolver(); + Settings.Secure.putInt(cr, Settings.Secure.NETWORK_PREFERENCE, preference); + synchronized(this) { + mNetworkPreference = preference; + } enforcePreference(); } } } - public int getNetworkPreference() { - enforceAccessPermission(); - return mNetworkPreference; - } - - private void persistNetworkPreference(int networkPreference) { - final ContentResolver cr = mContext.getContentResolver(); - Settings.Secure.putInt(cr, Settings.Secure.NETWORK_PREFERENCE, - networkPreference); - } - private int getPersistedNetworkPreference() { final ContentResolver cr = mContext.getContentResolver(); @@ -628,8 +699,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { mNetRequestersPids[usedNetworkType].add(currentPid); } } - mHandler.sendMessageDelayed(mHandler.obtainMessage( - NetworkStateTracker.EVENT_RESTORE_DEFAULT_NETWORK, + mHandler.sendMessageDelayed(mHandler.obtainMessage(EVENT_RESTORE_DEFAULT_NETWORK, f), getRestoreDefaultNetworkDelay()); @@ -871,15 +941,18 @@ public class ConnectivityService extends IConnectivityManager.Stub { android.Manifest.permission.CHANGE_BACKGROUND_DATA_SETTING, "ConnectivityService"); - if (getBackgroundDataSetting() == allowBackgroundDataUsage) return; - - Settings.Secure.putInt(mContext.getContentResolver(), - Settings.Secure.BACKGROUND_DATA, - allowBackgroundDataUsage ? 1 : 0); + mHandler.sendMessage(mHandler.obtainMessage(EVENT_SET_BACKGROUND_DATA, + (allowBackgroundDataUsage ? ENABLED : DISABLED), 0)); + } - Intent broadcast = new Intent( - ConnectivityManager.ACTION_BACKGROUND_DATA_SETTING_CHANGED); - mContext.sendBroadcast(broadcast); + private void handleSetBackgroundData(boolean enabled) { + if (enabled != getBackgroundDataSetting()) { + Settings.Secure.putInt(mContext.getContentResolver(), + Settings.Secure.BACKGROUND_DATA, enabled ? 1 : 0); + Intent broadcast = new Intent( + ConnectivityManager.ACTION_BACKGROUND_DATA_SETTING_CHANGED); + mContext.sendBroadcast(broadcast); + } } /** @@ -896,10 +969,15 @@ public class ConnectivityService extends IConnectivityManager.Stub { /** * @see ConnectivityManager#setMobileDataEnabled(boolean) */ - public synchronized void setMobileDataEnabled(boolean enabled) { + public void setMobileDataEnabled(boolean enabled) { enforceChangePermission(); if (DBG) Slog.d(TAG, "setMobileDataEnabled(" + enabled + ")"); + mHandler.sendMessage(mHandler.obtainMessage(EVENT_SET_MOBILE_DATA, + (enabled ? ENABLED : DISABLED), 0)); + } + + private void handleSetMobileData(boolean enabled) { if (getMobileDataEnabled() == enabled) return; Settings.Secure.putInt(mContext.getContentResolver(), @@ -907,7 +985,9 @@ public class ConnectivityService extends IConnectivityManager.Stub { if (enabled) { if (mNetTrackers[ConnectivityManager.TYPE_MOBILE] != null) { - if (DBG) Slog.d(TAG, "starting up " + mNetTrackers[ConnectivityManager.TYPE_MOBILE]); + if (DBG) { + Slog.d(TAG, "starting up " + mNetTrackers[ConnectivityManager.TYPE_MOBILE]); + } mNetTrackers[ConnectivityManager.TYPE_MOBILE].reconnect(); } } else { @@ -1267,7 +1347,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { // new network if (mNetTransitionWakeLock.isHeld()) { mHandler.sendMessageDelayed(mHandler.obtainMessage( - NetworkStateTracker.EVENT_CLEAR_NET_TRANSITION_WAKELOCK, + EVENT_CLEAR_NET_TRANSITION_WAKELOCK, mNetTransitionWakeLockSerialNumber, 0), 1000); } @@ -1705,11 +1785,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { type = info.getType(); handleDnsConfigurationChange(type); break; - case NetworkStateTracker.EVENT_RESTORE_DEFAULT_NETWORK: - FeatureUser u = (FeatureUser)msg.obj; - u.expire(); - break; - case NetworkStateTracker.EVENT_CLEAR_NET_TRANSITION_WAKELOCK: + case EVENT_CLEAR_NET_TRANSITION_WAKELOCK: String causedBy = null; synchronized (ConnectivityService.this) { if (msg.arg1 == mNetTransitionWakeLockSerialNumber && @@ -1723,70 +1799,42 @@ public class ConnectivityService extends IConnectivityManager.Stub { causedBy + " released by timeout"); } break; - case NetworkStateTracker.EVENT_INET_CONDITION_CHANGE: - if (DBG) { - Slog.d(TAG, "Inet connectivity change, net=" + - msg.arg1 + ", condition=" + msg.arg2 + - ",mActiveDefaultNetwork=" + mActiveDefaultNetwork); - } - if (mActiveDefaultNetwork == -1) { - if (DBG) Slog.d(TAG, "no active default network - aborting"); - break; - } - if (mActiveDefaultNetwork != msg.arg1) { - if (DBG) Slog.d(TAG, "given net not default - aborting"); - break; - } - mDefaultInetCondition = msg.arg2; - int delay; - if (mInetConditionChangeInFlight == false) { - if (DBG) Slog.d(TAG, "starting a change hold"); - // setup a new hold to debounce this - if (mDefaultInetCondition > 50) { - delay = Settings.Secure.getInt(mContext.getContentResolver(), - Settings.Secure.INET_CONDITION_DEBOUNCE_UP_DELAY, 500); - } else { - delay = Settings.Secure.getInt(mContext.getContentResolver(), - Settings.Secure.INET_CONDITION_DEBOUNCE_DOWN_DELAY, 3000); - } - mInetConditionChangeInFlight = true; - sendMessageDelayed(obtainMessage( - NetworkStateTracker.EVENT_INET_CONDITION_HOLD_END, - mActiveDefaultNetwork, mDefaultConnectionSequence), delay); - } else { - // we've set the new condition, when this hold ends that will get - // picked up - if (DBG) Slog.d(TAG, "currently in hold - not setting new end evt"); - } + case EVENT_RESTORE_DEFAULT_NETWORK: + FeatureUser u = (FeatureUser)msg.obj; + u.expire(); break; - case NetworkStateTracker.EVENT_INET_CONDITION_HOLD_END: - if (DBG) { - Slog.d(TAG, "Inet hold end, net=" + msg.arg1 + - ", condition =" + mDefaultInetCondition + - ", published condition =" + mDefaultInetConditionPublished); - } - mInetConditionChangeInFlight = false; - - if (mActiveDefaultNetwork == -1) { - if (DBG) Slog.d(TAG, "no active default network - aborting"); - break; - } - if (mDefaultConnectionSequence != msg.arg2) { - if (DBG) Slog.d(TAG, "event hold for obsolete network - aborting"); - break; - } - if (mDefaultInetConditionPublished == mDefaultInetCondition) { - if (DBG) Slog.d(TAG, "no change in condition - aborting"); - break; - } - NetworkInfo networkInfo = mNetTrackers[mActiveDefaultNetwork].getNetworkInfo(); - if (networkInfo.isConnected() == false) { - if (DBG) Slog.d(TAG, "default network not connected - aborting"); - break; - } - mDefaultInetConditionPublished = mDefaultInetCondition; - sendInetConditionBroadcast(networkInfo); + case EVENT_INET_CONDITION_CHANGE: + { + int netType = msg.arg1; + int condition = msg.arg2; + handleInetConditionChange(netType, condition); + break; + } + case EVENT_INET_CONDITION_HOLD_END: + { + int netType = msg.arg1; + int sequence = msg.arg2; + handleInetConditionHoldEnd(netType, sequence); + break; + } + case EVENT_SET_NETWORK_PREFERENCE: + { + int preference = msg.arg1; + handleSetNetworkPreference(preference); + break; + } + case EVENT_SET_BACKGROUND_DATA: + { + boolean enabled = (msg.arg1 == ENABLED); + handleSetBackgroundData(enabled); + break; + } + case EVENT_SET_MOBILE_DATA: + { + boolean enabled = (msg.arg1 == ENABLED); + handleSetMobileData(enabled); break; + } } } } @@ -1893,7 +1941,7 @@ public class ConnectivityService extends IConnectivityManager.Stub { mNetTransitionWakeLockCausedBy = forWhom; } mHandler.sendMessageDelayed(mHandler.obtainMessage( - NetworkStateTracker.EVENT_CLEAR_NET_TRANSITION_WAKELOCK, + EVENT_CLEAR_NET_TRANSITION_WAKELOCK, mNetTransitionWakeLockSerialNumber, 0), mNetTransitionWakeLockTimeout); return; @@ -1918,6 +1966,72 @@ public class ConnectivityService extends IConnectivityManager.Stub { } } mHandler.sendMessage(mHandler.obtainMessage( - NetworkStateTracker.EVENT_INET_CONDITION_CHANGE, networkType, percentage)); + EVENT_INET_CONDITION_CHANGE, networkType, percentage)); + } + + private void handleInetConditionChange(int netType, int condition) { + if (DBG) { + Slog.d(TAG, "Inet connectivity change, net=" + + netType + ", condition=" + condition + + ",mActiveDefaultNetwork=" + mActiveDefaultNetwork); + } + if (mActiveDefaultNetwork == -1) { + if (DBG) Slog.d(TAG, "no active default network - aborting"); + return; + } + if (mActiveDefaultNetwork != netType) { + if (DBG) Slog.d(TAG, "given net not default - aborting"); + return; + } + mDefaultInetCondition = condition; + int delay; + if (mInetConditionChangeInFlight == false) { + if (DBG) Slog.d(TAG, "starting a change hold"); + // setup a new hold to debounce this + if (mDefaultInetCondition > 50) { + delay = Settings.Secure.getInt(mContext.getContentResolver(), + Settings.Secure.INET_CONDITION_DEBOUNCE_UP_DELAY, 500); + } else { + delay = Settings.Secure.getInt(mContext.getContentResolver(), + Settings.Secure.INET_CONDITION_DEBOUNCE_DOWN_DELAY, 3000); + } + mInetConditionChangeInFlight = true; + mHandler.sendMessageDelayed(mHandler.obtainMessage(EVENT_INET_CONDITION_HOLD_END, + mActiveDefaultNetwork, mDefaultConnectionSequence), delay); + } else { + // we've set the new condition, when this hold ends that will get + // picked up + if (DBG) Slog.d(TAG, "currently in hold - not setting new end evt"); + } + } + + private void handleInetConditionHoldEnd(int netType, int sequence) { + if (DBG) { + Slog.d(TAG, "Inet hold end, net=" + netType + + ", condition =" + mDefaultInetCondition + + ", published condition =" + mDefaultInetConditionPublished); + } + mInetConditionChangeInFlight = false; + + if (mActiveDefaultNetwork == -1) { + if (DBG) Slog.d(TAG, "no active default network - aborting"); + return; + } + if (mDefaultConnectionSequence != sequence) { + if (DBG) Slog.d(TAG, "event hold for obsolete network - aborting"); + return; + } + if (mDefaultInetConditionPublished == mDefaultInetCondition) { + if (DBG) Slog.d(TAG, "no change in condition - aborting"); + return; + } + NetworkInfo networkInfo = mNetTrackers[mActiveDefaultNetwork].getNetworkInfo(); + if (networkInfo.isConnected() == false) { + if (DBG) Slog.d(TAG, "default network not connected - aborting"); + return; + } + mDefaultInetConditionPublished = mDefaultInetCondition; + sendInetConditionBroadcast(networkInfo); + return; } } diff --git a/services/java/com/android/server/ProcessStats.java b/services/java/com/android/server/ProcessStats.java index 5bdadcc..43dbcc0 100644 --- a/services/java/com/android/server/ProcessStats.java +++ b/services/java/com/android/server/ProcessStats.java @@ -80,16 +80,24 @@ public class ProcessStats { PROC_SPACE_TERM|PROC_OUT_LONG, // 11: major faults PROC_SPACE_TERM, PROC_SPACE_TERM|PROC_OUT_LONG, // 13: utime - PROC_SPACE_TERM|PROC_OUT_LONG // 14: stime + PROC_SPACE_TERM|PROC_OUT_LONG, // 14: stime + PROC_SPACE_TERM, + PROC_SPACE_TERM, + PROC_SPACE_TERM, + PROC_SPACE_TERM, + PROC_SPACE_TERM, + PROC_SPACE_TERM, + PROC_SPACE_TERM|PROC_OUT_LONG, // 21: vsize }; static final int PROCESS_FULL_STAT_MINOR_FAULTS = 1; static final int PROCESS_FULL_STAT_MAJOR_FAULTS = 2; static final int PROCESS_FULL_STAT_UTIME = 3; static final int PROCESS_FULL_STAT_STIME = 4; + static final int PROCESS_FULL_STAT_VSIZE = 5; - private final String[] mProcessFullStatsStringData = new String[5]; - private final long[] mProcessFullStatsData = new long[5]; + private final String[] mProcessFullStatsStringData = new String[6]; + private final long[] mProcessFullStatsData = new long[6]; private static final int[] SYSTEM_CPU_FORMAT = new int[] { PROC_SPACE_TERM|PROC_COMBINE, @@ -171,6 +179,8 @@ public class ProcessStats { final ArrayList<Stats> threadStats; final ArrayList<Stats> workingThreads; + public boolean interesting; + public String baseName; public String name; int nameWidth; @@ -349,59 +359,62 @@ public class ProcessStats { + (parentPid < 0 ? "process" : "thread") + " pid " + pid + ": " + st); - final long uptime = SystemClock.uptimeMillis(); + if (st.interesting) { + final long uptime = SystemClock.uptimeMillis(); - final long[] procStats = mProcessStatsData; - if (!Process.readProcFile(st.statFile.toString(), - PROCESS_STATS_FORMAT, null, procStats, null)) { - continue; - } - - final long minfaults = procStats[PROCESS_STAT_MINOR_FAULTS]; - final long majfaults = procStats[PROCESS_STAT_MAJOR_FAULTS]; - final long utime = procStats[PROCESS_STAT_UTIME]; - final long stime = procStats[PROCESS_STAT_STIME]; - - if (utime == st.base_utime && stime == st.base_stime) { - st.rel_utime = 0; - st.rel_stime = 0; - st.rel_minfaults = 0; - st.rel_majfaults = 0; - if (st.active) { - st.active = false; + final long[] procStats = mProcessStatsData; + if (!Process.readProcFile(st.statFile.toString(), + PROCESS_STATS_FORMAT, null, procStats, null)) { + continue; } - continue; - } - if (!st.active) { - st.active = true; - } + final long minfaults = procStats[PROCESS_STAT_MINOR_FAULTS]; + final long majfaults = procStats[PROCESS_STAT_MAJOR_FAULTS]; + final long utime = procStats[PROCESS_STAT_UTIME]; + final long stime = procStats[PROCESS_STAT_STIME]; + + if (utime == st.base_utime && stime == st.base_stime) { + st.rel_utime = 0; + st.rel_stime = 0; + st.rel_minfaults = 0; + st.rel_majfaults = 0; + if (st.active) { + st.active = false; + } + continue; + } - if (parentPid < 0) { - getName(st, st.cmdlineFile); - if (st.threadStats != null) { - mCurThreadPids = collectStats(st.threadsDir, pid, false, - mCurThreadPids, st.threadStats); + if (!st.active) { + st.active = true; } + + if (parentPid < 0) { + getName(st, st.cmdlineFile); + if (st.threadStats != null) { + mCurThreadPids = collectStats(st.threadsDir, pid, false, + mCurThreadPids, st.threadStats); + } + } + + if (DEBUG) Slog.v("Load", "Stats changed " + st.name + " pid=" + st.pid + + " utime=" + utime + "-" + st.base_utime + + " stime=" + stime + "-" + st.base_stime + + " minfaults=" + minfaults + "-" + st.base_minfaults + + " majfaults=" + majfaults + "-" + st.base_majfaults); + + st.rel_uptime = uptime - st.base_uptime; + st.base_uptime = uptime; + st.rel_utime = (int)(utime - st.base_utime); + st.rel_stime = (int)(stime - st.base_stime); + st.base_utime = utime; + st.base_stime = stime; + st.rel_minfaults = (int)(minfaults - st.base_minfaults); + st.rel_majfaults = (int)(majfaults - st.base_majfaults); + st.base_minfaults = minfaults; + st.base_majfaults = majfaults; + st.working = true; } - if (DEBUG) Slog.v("Load", "Stats changed " + st.name + " pid=" + st.pid - + " utime=" + utime + "-" + st.base_utime - + " stime=" + stime + "-" + st.base_stime - + " minfaults=" + minfaults + "-" + st.base_minfaults - + " majfaults=" + majfaults + "-" + st.base_majfaults); - - st.rel_uptime = uptime - st.base_uptime; - st.base_uptime = uptime; - st.rel_utime = (int)(utime - st.base_utime); - st.rel_stime = (int)(stime - st.base_stime); - st.base_utime = utime; - st.base_stime = stime; - st.rel_minfaults = (int)(minfaults - st.base_minfaults); - st.rel_majfaults = (int)(majfaults - st.base_majfaults); - st.base_minfaults = minfaults; - st.base_majfaults = majfaults; - st.working = true; continue; } @@ -421,12 +434,24 @@ public class ProcessStats { if (Process.readProcFile(st.statFile.toString(), PROCESS_FULL_STATS_FORMAT, procStatsString, procStats, null)) { - st.baseName = procStatsString[0]; - st.base_minfaults = procStats[PROCESS_FULL_STAT_MINOR_FAULTS]; - st.base_majfaults = procStats[PROCESS_FULL_STAT_MAJOR_FAULTS]; - st.base_utime = procStats[PROCESS_FULL_STAT_UTIME]; - st.base_stime = procStats[PROCESS_FULL_STAT_STIME]; + // This is a possible way to filter out processes that + // are actually kernel threads... do we want to? Some + // of them do use CPU, but there can be a *lot* that are + // not doing anything. + if (true || procStats[PROCESS_FULL_STAT_VSIZE] != 0) { + st.interesting = true; + st.baseName = procStatsString[0]; + st.base_minfaults = procStats[PROCESS_FULL_STAT_MINOR_FAULTS]; + st.base_majfaults = procStats[PROCESS_FULL_STAT_MAJOR_FAULTS]; + st.base_utime = procStats[PROCESS_FULL_STAT_UTIME]; + st.base_stime = procStats[PROCESS_FULL_STAT_STIME]; + } else { + Slog.i(TAG, "Skipping kernel process pid " + pid + + " name " + procStatsString[0]); + st.baseName = procStatsString[0]; + } } else { + Slog.w(TAG, "Skipping unknown process pid " + pid); st.baseName = "<unknown>"; st.base_utime = st.base_stime = 0; st.base_minfaults = st.base_majfaults = 0; @@ -438,7 +463,7 @@ public class ProcessStats { mCurThreadPids = collectStats(st.threadsDir, pid, true, mCurThreadPids, st.threadStats); } - } else { + } else if (st.interesting) { st.name = st.baseName; st.nameWidth = onMeasureProcessName(st.name); } @@ -452,7 +477,7 @@ public class ProcessStats { st.rel_minfaults = 0; st.rel_majfaults = 0; st.added = true; - if (!first) { + if (!first && st.interesting) { st.working = true; } continue; @@ -624,6 +649,14 @@ public class ProcessStats { } } + final public int countStats() { + return mProcStats.size(); + } + + final public Stats getStats(int index) { + return mProcStats.get(index); + } + final public int countWorkingStats() { buildWorkingProcs(); return mWorkingProcs.size(); @@ -788,7 +821,8 @@ public class ProcessStats { private void getName(Stats st, String cmdlineFile) { String newName = st.name; - if (st.name == null || st.name.equals("app_process")) { + if (st.name == null || st.name.equals("app_process") + || st.name.equals("<pre-initialized>")) { String cmdName = readFile(cmdlineFile, '\0'); if (cmdName != null && cmdName.length() > 1) { newName = cmdName; diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java index 3e4f522..7100cc5 100644 --- a/services/java/com/android/server/WindowManagerService.java +++ b/services/java/com/android/server/WindowManagerService.java @@ -5687,9 +5687,12 @@ public class WindowManagerService extends IWindowManager.Stub int requestedWidth, int requestedHeight, int viewFlags, boolean insetsPending, Rect outFrame, Rect outContentInsets, Rect outVisibleInsets, Configuration outConfig, Surface outSurface) { - return relayoutWindow(this, window, attrs, + //Log.d(TAG, ">>>>>> ENTERED relayout from " + Binder.getCallingPid()); + int res = relayoutWindow(this, window, attrs, requestedWidth, requestedHeight, viewFlags, insetsPending, outFrame, outContentInsets, outVisibleInsets, outConfig, outSurface); + //Log.d(TAG, "<<<<<< EXITING relayout to " + Binder.getCallingPid()); + return res; } public void setTransparentRegion(IWindow window, Region region) { diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index c896c94..9685fb7 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -122,6 +122,8 @@ import java.io.PrintWriter; import java.lang.IllegalStateException; import java.lang.ref.WeakReference; import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; @@ -145,6 +147,7 @@ public final class ActivityManagerService extends ActivityManagerNative 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_SERVICE_EXECUTING = localLOGV || false; static final boolean DEBUG_VISBILITY = localLOGV || false; static final boolean DEBUG_PROCESSES = localLOGV || false; static final boolean DEBUG_PROVIDER = localLOGV || false; @@ -153,6 +156,8 @@ public final class ActivityManagerService extends ActivityManagerNative static final boolean DEBUG_RESULTS = localLOGV || false; static final boolean DEBUG_BACKUP = localLOGV || false; static final boolean DEBUG_CONFIGURATION = localLOGV || false; + static final boolean DEBUG_POWER = localLOGV || false; + static final boolean DEBUG_POWER_QUICK = DEBUG_POWER || false; static final boolean VALIDATE_TOKENS = false; static final boolean SHOW_ACTIVITY_START_TIME = true; @@ -198,8 +203,16 @@ public final class ActivityManagerService extends ActivityManagerNative // The minimum amount of time between successive GC requests for a process. static final int GC_MIN_INTERVAL = 60*1000; - // The rate at which we check for apps using excessive wake locks -- 15 mins. - static final int WAKE_LOCK_CHECK_DELAY = 15*60*1000; + // The rate at which we check for apps using excessive power -- 15 mins. + static final int POWER_CHECK_DELAY = (DEBUG_POWER_QUICK ? 2 : 15) * 60*1000; + + // The minimum sample duration we will allow before deciding we have + // enough data on wake locks to start killing things. + static final int WAKE_LOCK_MIN_CHECK_DURATION = (DEBUG_POWER_QUICK ? 1 : 5) * 60*1000; + + // The minimum sample duration we will allow before deciding we have + // enough data on CPU usage to start killing things. + static final int CPU_MIN_CHECK_DURATION = (DEBUG_POWER_QUICK ? 1 : 5) * 60*1000; // How long we allow a receiver to run before giving up on it. static final int BROADCAST_TIMEOUT = 10*1000; @@ -553,6 +566,11 @@ public final class ActivityManagerService extends ActivityManagerNative private final StringBuilder mStrictModeBuffer = new StringBuilder(); /** + * True if we have a pending unexpired BROADCAST_TIMEOUT_MSG posted to our handler. + */ + private boolean mPendingBroadcastTimeoutMessage; + + /** * Intent broadcast that we have tried to start, but are * waiting for its application's process to be created. We only * need one (instead of a list) because we always process broadcasts @@ -780,9 +798,14 @@ public final class ActivityManagerService extends ActivityManagerNative boolean mDidAppSwitch; /** - * Last time (in realtime) at which we checked for wake lock usage. + * Last time (in realtime) at which we checked for power usage. + */ + long mLastPowerCheckRealtime; + + /** + * Last time (in uptime) at which we checked for power usage. */ - long mLastWakeLockCheckTime; + long mLastPowerCheckUptime; /** * Set while we are wanting to sleep, to prevent any @@ -1053,17 +1076,8 @@ public final class ActivityManagerService extends ActivityManagerNative processNextBroadcast(true); } break; case BROADCAST_TIMEOUT_MSG: { - if (mDidDexOpt) { - mDidDexOpt = false; - Message nmsg = mHandler.obtainMessage(BROADCAST_TIMEOUT_MSG); - mHandler.sendMessageDelayed(nmsg, BROADCAST_TIMEOUT); - return; - } - // Only process broadcast timeouts if the system is ready. That way - // PRE_BOOT_COMPLETED broadcasts can't timeout as they are intended - // to do heavy lifting for system up - if (mProcessesReady) { - broadcastTimeout(); + synchronized (ActivityManagerService.this) { + broadcastTimeoutLocked(true); } } break; case SERVICE_TIMEOUT_MSG: { @@ -1195,12 +1209,10 @@ public final class ActivityManagerService extends ActivityManagerNative } break; case CHECK_EXCESSIVE_WAKE_LOCKS_MSG: { synchronized (ActivityManagerService.this) { - checkExcessiveWakeLocksLocked(true); + checkExcessivePowerUsageLocked(true); removeMessages(CHECK_EXCESSIVE_WAKE_LOCKS_MSG); - if (mSleeping) { - Message nmsg = obtainMessage(CHECK_EXCESSIVE_WAKE_LOCKS_MSG); - sendMessageDelayed(nmsg, WAKE_LOCK_CHECK_DELAY); - } + Message nmsg = obtainMessage(CHECK_EXCESSIVE_WAKE_LOCKS_MSG); + sendMessageDelayed(nmsg, POWER_CHECK_DELAY); } } break; } @@ -1395,7 +1407,8 @@ public final class ActivityManagerService extends ActivityManagerNative systemDir, "batterystats.bin").toString()); mBatteryStatsService.getActiveStatistics().readLocked(); mBatteryStatsService.getActiveStatistics().writeAsyncLocked(); - mOnBattery = mBatteryStatsService.getActiveStatistics().getIsOnBattery(); + mOnBattery = DEBUG_POWER ? true + : mBatteryStatsService.getActiveStatistics().getIsOnBattery(); mBatteryStatsService.getActiveStatistics().setCallback(this); mUsageStatsService = new UsageStatsService(new File( @@ -1515,10 +1528,12 @@ public final class ActivityManagerService extends ActivityManagerNative int perc = bstats.startAddingCpuLocked(); int totalUTime = 0; int totalSTime = 0; - final int N = mProcessStats.countWorkingStats(); + final int N = mProcessStats.countStats(); for (int i=0; i<N; i++) { - ProcessStats.Stats st - = mProcessStats.getWorkingStats(i); + ProcessStats.Stats st = mProcessStats.getStats(i); + if (!st.working) { + continue; + } ProcessRecord pr = mPidsSelfLocked.get(st.pid); int otherUTime = (st.rel_utime*perc)/100; int otherSTime = (st.rel_stime*perc)/100; @@ -1529,6 +1544,7 @@ public final class ActivityManagerService extends ActivityManagerNative ps.addCpuTimeLocked(st.rel_utime-otherUTime, st.rel_stime-otherSTime); ps.addSpeedStepTimes(cpuSpeedTimes); + pr.curCpuTime += (st.rel_utime+st.rel_stime) * 10; } else { BatteryStatsImpl.Uid.Proc ps = bstats.getProcessStatsLocked(st.name, st.pid); @@ -1565,7 +1581,7 @@ public final class ActivityManagerService extends ActivityManagerNative updateCpuStatsNow(); synchronized (this) { synchronized(mPidsSelfLocked) { - mOnBattery = onBattery; + mOnBattery = DEBUG_POWER ? true : onBattery; } } } @@ -2790,11 +2806,36 @@ public final class ActivityManagerService extends ActivityManagerNative } } + private final class AppNotResponding implements Runnable { + private final ProcessRecord mApp; + private final String mAnnotation; + + public AppNotResponding(ProcessRecord app, String annotation) { + mApp = app; + mAnnotation = annotation; + } + + @Override + public void run() { + appNotResponding(mApp, null, null, mAnnotation); + } + } + final void appNotResponding(ProcessRecord app, ActivityRecord activity, ActivityRecord parent, final String annotation) { ArrayList<Integer> firstPids = new ArrayList<Integer>(5); SparseArray<Boolean> lastPids = new SparseArray<Boolean>(20); + if (mController != null) { + try { + // 0 == continue, -1 = kill process immediately + int res = mController.appEarlyNotResponding(app.processName, app.pid, annotation); + if (res < 0 && app.pid != MY_PID) Process.killProcess(app.pid); + } catch (RemoteException e) { + mController = null; + } + } + long anrTime = SystemClock.uptimeMillis(); if (MONITOR_CPU_USAGE) { updateCpuStatsNow(); @@ -2845,10 +2886,6 @@ public final class ActivityManagerService extends ActivityManagerNative } } - final ProcessStats processStats = new ProcessStats(true); - - File tracesFile = dumpStackTraces(true, firstPids, processStats, lastPids); - // Log the ANR to the main log. StringBuilder info = mStringBuilder; info.setLength(0); @@ -2864,6 +2901,10 @@ public final class ActivityManagerService extends ActivityManagerNative info.append("Parent: ").append(parent.shortComponentName).append("\n"); } + final ProcessStats processStats = new ProcessStats(true); + + File tracesFile = dumpStackTraces(true, firstPids, processStats, lastPids); + String cpuInfo = null; if (MONITOR_CPU_USAGE) { updateCpuStatsNow(); @@ -3648,7 +3689,7 @@ public final class ActivityManagerService extends ActivityManagerNative Slog.w(TAG, "Exception in new application when starting receiver " + br.curComponent.flattenToShortString(), e); badApp = true; - logBroadcastReceiverDiscard(br); + logBroadcastReceiverDiscardLocked(br); finishReceiverLocked(br.receiver, br.resultCode, br.resultData, br.resultExtras, br.resultAbort, true); scheduleBroadcastsLocked(); @@ -3742,7 +3783,7 @@ public final class ActivityManagerService extends ActivityManagerNative if (mFactoryTest != SystemServer.FACTORY_TEST_LOW_LEVEL) { // Start looking for apps that are abusing wake locks. Message nmsg = mHandler.obtainMessage(CHECK_EXCESSIVE_WAKE_LOCKS_MSG); - mHandler.sendMessageDelayed(nmsg, WAKE_LOCK_CHECK_DELAY); + mHandler.sendMessageDelayed(nmsg, POWER_CHECK_DELAY); // Tell anyone interested that we are done booting! SystemProperties.set("sys.boot_completed", "1"); broadcastIntentLocked(null, null, @@ -5655,10 +5696,10 @@ public final class ActivityManagerService extends ActivityManagerNative } // Initialize the wake times of all processes. - checkExcessiveWakeLocksLocked(false); + checkExcessivePowerUsageLocked(false); mHandler.removeMessages(CHECK_EXCESSIVE_WAKE_LOCKS_MSG); Message nmsg = mHandler.obtainMessage(CHECK_EXCESSIVE_WAKE_LOCKS_MSG); - mHandler.sendMessageDelayed(nmsg, WAKE_LOCK_CHECK_DELAY); + mHandler.sendMessageDelayed(nmsg, POWER_CHECK_DELAY); } } @@ -5708,7 +5749,6 @@ public final class ActivityManagerService extends ActivityManagerNative mWindowManager.setEventDispatching(true); mSleeping = false; mMainStack.resumeTopActivityLocked(null); - mHandler.removeMessages(CHECK_EXCESSIVE_WAKE_LOCKS_MSG); } } @@ -6377,7 +6417,7 @@ public final class ActivityManagerService extends ActivityManagerNative // The current broadcast is waiting for this app's receiver // to be finished. Looks like that's not going to happen, so // let the broadcast continue. - logBroadcastReceiverDiscard(r); + logBroadcastReceiverDiscardLocked(r); finishReceiverLocked(r.receiver, r.resultCode, r.resultData, r.resultExtras, r.resultAbort, true); reschedule = true; @@ -6386,7 +6426,7 @@ public final class ActivityManagerService extends ActivityManagerNative if (r != null && r.curApp == app) { if (DEBUG_BROADCAST) Slog.v(TAG, "skip & discard pending app " + r); - logBroadcastReceiverDiscard(r); + logBroadcastReceiverDiscardLocked(r); finishReceiverLocked(r.receiver, r.resultCode, r.resultData, r.resultExtras, r.resultAbort, true); reschedule = true; @@ -7055,12 +7095,13 @@ public final class ActivityManagerService extends ActivityManagerNative pw.println("Activity manager dump options:"); pw.println(" [-a] [-h] [cmd] ..."); pw.println(" cmd may be one of:"); - pw.println(" activities: activity stack state"); - pw.println(" broadcasts: broadcast state"); - pw.println(" intents: pending intent state"); - pw.println(" processes: process state"); - pw.println(" providers: content provider state"); - pw.println(" services: service state"); + pw.println(" a[ctivities]: activity stack state"); + pw.println(" b[roadcasts]: broadcast state"); + pw.println(" i[ntents]: pending intent state"); + pw.println(" p[rocesses]: process state"); + pw.println(" o[om]: out of memory management"); + pw.println(" prov[iders]: content provider state"); + pw.println(" s[ervices]: service state"); pw.println(" service [name]: service client-side state"); return; } else { @@ -7092,6 +7133,11 @@ public final class ActivityManagerService extends ActivityManagerNative dumpProcessesLocked(fd, pw, args, opti, true); } return; + } else if ("oom".equals(cmd) || "o".equals(cmd)) { + synchronized (this) { + dumpOomLocked(fd, pw, args, opti, true); + } + return; } else if ("providers".equals(cmd) || "prov".equals(cmd)) { synchronized (this) { dumpProvidersLocked(fd, pw, args, opti, true); @@ -7213,7 +7259,7 @@ public final class ActivityManagerService extends ActivityManagerNative return true; } - + boolean dumpProcessesLocked(FileDescriptor fd, PrintWriter pw, String[] args, int opti, boolean dumpAll) { boolean needSep = false; @@ -7243,8 +7289,8 @@ public final class ActivityManagerService extends ActivityManagerNative if (needSep) pw.println(" "); needSep = true; pw.println(" Running processes (most recent first):"); - dumpProcessList(pw, this, mLruProcesses, " ", - "Proc", "PERS", true); + dumpProcessOomList(pw, this, mLruProcesses, " ", + "Proc", "PERS", false); needSep = true; } @@ -7275,7 +7321,7 @@ public final class ActivityManagerService extends ActivityManagerNative needSep = true; pw.println(" Persisent processes that are starting:"); dumpProcessList(pw, this, mPersistentStartingProcesses, " ", - "Starting Norm", "Restarting PERS", false); + "Starting Norm", "Restarting PERS"); } if (mStartingProcesses.size() > 0) { @@ -7283,7 +7329,7 @@ public final class ActivityManagerService extends ActivityManagerNative needSep = true; pw.println(" Processes that are starting:"); dumpProcessList(pw, this, mStartingProcesses, " ", - "Starting Norm", "Starting PERS", false); + "Starting Norm", "Starting PERS"); } if (mRemovedProcesses.size() > 0) { @@ -7291,7 +7337,7 @@ public final class ActivityManagerService extends ActivityManagerNative needSep = true; pw.println(" Processes that are being removed:"); dumpProcessList(pw, this, mRemovedProcesses, " ", - "Removed Norm", "Removed PERS", false); + "Removed Norm", "Removed PERS"); } if (mProcessesOnHold.size() > 0) { @@ -7299,26 +7345,10 @@ public final class ActivityManagerService extends ActivityManagerNative needSep = true; pw.println(" Processes that are on old until the system is ready:"); dumpProcessList(pw, this, mProcessesOnHold, " ", - "OnHold Norm", "OnHold PERS", false); + "OnHold Norm", "OnHold PERS"); } - if (mProcessesToGc.size() > 0) { - if (needSep) pw.println(" "); - needSep = true; - pw.println(" Processes that are waiting to GC:"); - long now = SystemClock.uptimeMillis(); - for (int i=0; i<mProcessesToGc.size(); i++) { - ProcessRecord proc = mProcessesToGc.get(i); - pw.print(" Process "); pw.println(proc); - pw.print(" lowMem="); pw.print(proc.reportLowMemory); - pw.print(", last gced="); - pw.print(now-proc.lastRequestedGc); - pw.print(" ms ago, last lowMem="); - pw.print(now-proc.lastLowMemory); - pw.println(" ms ago"); - - } - } + needSep = dumpProcessesToGc(fd, pw, args, opti, needSep, dumpAll); if (mProcessCrashTimes.getMap().size() > 0) { if (needSep) pw.println(" "); @@ -7382,6 +7412,12 @@ public final class ActivityManagerService extends ActivityManagerNative pw.println(" mBooting=" + mBooting + " mBooted=" + mBooted + " mFactoryTest=" + mFactoryTest); + pw.print(" mLastPowerCheckRealtime="); + TimeUtils.formatDuration(mLastPowerCheckRealtime, pw); + pw.println(""); + pw.print(" mLastPowerCheckUptime="); + TimeUtils.formatDuration(mLastPowerCheckUptime, pw); + pw.println(""); pw.println(" mGoingToSleep=" + mMainStack.mGoingToSleep); pw.println(" mLaunchingActivity=" + mMainStack.mLaunchingActivity); pw.println(" mAdjSeq=" + mAdjSeq + " mLruSeq=" + mLruSeq); @@ -7390,6 +7426,75 @@ public final class ActivityManagerService extends ActivityManagerNative return true; } + boolean dumpProcessesToGc(FileDescriptor fd, PrintWriter pw, String[] args, + int opti, boolean needSep, boolean dumpAll) { + if (mProcessesToGc.size() > 0) { + if (needSep) pw.println(" "); + needSep = true; + pw.println(" Processes that are waiting to GC:"); + long now = SystemClock.uptimeMillis(); + for (int i=0; i<mProcessesToGc.size(); i++) { + ProcessRecord proc = mProcessesToGc.get(i); + pw.print(" Process "); pw.println(proc); + pw.print(" lowMem="); pw.print(proc.reportLowMemory); + pw.print(", last gced="); + pw.print(now-proc.lastRequestedGc); + pw.print(" ms ago, last lowMem="); + pw.print(now-proc.lastLowMemory); + pw.println(" ms ago"); + + } + } + return needSep; + } + + boolean dumpOomLocked(FileDescriptor fd, PrintWriter pw, String[] args, + int opti, boolean dumpAll) { + boolean needSep = false; + + if (mLruProcesses.size() > 0) { + ArrayList<ProcessRecord> procs = new ArrayList<ProcessRecord>(mLruProcesses); + + Comparator<ProcessRecord> comparator = new Comparator<ProcessRecord>() { + @Override + public int compare(ProcessRecord object1, ProcessRecord object2) { + if (object1.setAdj != object2.setAdj) { + return object1.setAdj > object2.setAdj ? -1 : 1; + } + if (object1.setSchedGroup != object2.setSchedGroup) { + return object1.setSchedGroup > object2.setSchedGroup ? -1 : 1; + } + if (object1.keeping != object2.keeping) { + return object1.keeping ? -1 : 1; + } + if (object1.pid != object2.pid) { + return object1.pid > object2.pid ? -1 : 1; + } + return 0; + } + }; + + Collections.sort(procs, comparator); + + if (needSep) pw.println(" "); + needSep = true; + pw.println(" Process OOM control:"); + dumpProcessOomList(pw, this, procs, " ", + "Proc", "PERS", true); + needSep = true; + } + + needSep = dumpProcessesToGc(fd, pw, args, opti, needSep, dumpAll); + + pw.println(" "); + pw.println(" mHomeProcess: " + mHomeProcess); + if (mHeavyWeightProcess != null) { + pw.println(" mHeavyWeightProcess: " + mHeavyWeightProcess); + } + + return true; + } + /** * There are three ways to call this: * - no service specified: dump all the services @@ -7833,89 +7938,145 @@ public final class ActivityManagerService extends ActivityManagerNative private static final int dumpProcessList(PrintWriter pw, ActivityManagerService service, List list, - String prefix, String normalLabel, String persistentLabel, - boolean inclOomAdj) { + String prefix, String normalLabel, String persistentLabel) { int numPers = 0; final int N = list.size()-1; for (int i=N; i>=0; i--) { ProcessRecord r = (ProcessRecord)list.get(i); - if (false) { - pw.println(prefix + (r.persistent ? persistentLabel : normalLabel) - + " #" + i + ":"); - r.dump(pw, prefix + " "); - } else if (inclOomAdj) { - String oomAdj; - if (r.setAdj >= EMPTY_APP_ADJ) { - oomAdj = buildOomTag("empty", null, r.setAdj, EMPTY_APP_ADJ); - } else if (r.setAdj >= HIDDEN_APP_MIN_ADJ) { - oomAdj = buildOomTag("bak", " ", r.setAdj, HIDDEN_APP_MIN_ADJ); - } else if (r.setAdj >= HOME_APP_ADJ) { - oomAdj = buildOomTag("home ", null, r.setAdj, HOME_APP_ADJ); - } else if (r.setAdj >= SECONDARY_SERVER_ADJ) { - oomAdj = buildOomTag("svc", " ", r.setAdj, SECONDARY_SERVER_ADJ); - } else if (r.setAdj >= BACKUP_APP_ADJ) { - oomAdj = buildOomTag("bckup", null, r.setAdj, BACKUP_APP_ADJ); - } else if (r.setAdj >= HEAVY_WEIGHT_APP_ADJ) { - oomAdj = buildOomTag("hvy ", null, r.setAdj, HEAVY_WEIGHT_APP_ADJ); - } else if (r.setAdj >= PERCEPTIBLE_APP_ADJ) { - oomAdj = buildOomTag("prcp ", null, r.setAdj, PERCEPTIBLE_APP_ADJ); - } else if (r.setAdj >= VISIBLE_APP_ADJ) { - oomAdj = buildOomTag("vis ", null, r.setAdj, VISIBLE_APP_ADJ); - } else if (r.setAdj >= FOREGROUND_APP_ADJ) { - oomAdj = buildOomTag("fore ", null, r.setAdj, FOREGROUND_APP_ADJ); - } else if (r.setAdj >= CORE_SERVER_ADJ) { - oomAdj = buildOomTag("core ", null, r.setAdj, CORE_SERVER_ADJ); - } else if (r.setAdj >= SYSTEM_ADJ) { - oomAdj = buildOomTag("sys ", null, r.setAdj, SYSTEM_ADJ); + pw.println(String.format("%s%s #%2d: %s", + prefix, (r.persistent ? persistentLabel : normalLabel), + i, r.toString())); + if (r.persistent) { + numPers++; + } + } + return numPers; + } + + private static final void dumpProcessOomList(PrintWriter pw, + ActivityManagerService service, List<ProcessRecord> list, + String prefix, String normalLabel, String persistentLabel, + boolean inclDetails) { + + final long curRealtime = SystemClock.elapsedRealtime(); + final long realtimeSince = curRealtime - service.mLastPowerCheckRealtime; + final long curUptime = SystemClock.uptimeMillis(); + final long uptimeSince = curUptime - service.mLastPowerCheckUptime; + + final int N = list.size()-1; + for (int i=N; i>=0; i--) { + ProcessRecord r = list.get(i); + String oomAdj; + if (r.setAdj >= EMPTY_APP_ADJ) { + oomAdj = buildOomTag("empty", null, r.setAdj, EMPTY_APP_ADJ); + } else if (r.setAdj >= HIDDEN_APP_MIN_ADJ) { + oomAdj = buildOomTag("bak", " ", r.setAdj, HIDDEN_APP_MIN_ADJ); + } else if (r.setAdj >= HOME_APP_ADJ) { + oomAdj = buildOomTag("home ", null, r.setAdj, HOME_APP_ADJ); + } else if (r.setAdj >= SECONDARY_SERVER_ADJ) { + oomAdj = buildOomTag("svc", " ", r.setAdj, SECONDARY_SERVER_ADJ); + } else if (r.setAdj >= BACKUP_APP_ADJ) { + oomAdj = buildOomTag("bckup", null, r.setAdj, BACKUP_APP_ADJ); + } else if (r.setAdj >= HEAVY_WEIGHT_APP_ADJ) { + oomAdj = buildOomTag("hvy ", null, r.setAdj, HEAVY_WEIGHT_APP_ADJ); + } else if (r.setAdj >= PERCEPTIBLE_APP_ADJ) { + oomAdj = buildOomTag("prcp ", null, r.setAdj, PERCEPTIBLE_APP_ADJ); + } else if (r.setAdj >= VISIBLE_APP_ADJ) { + oomAdj = buildOomTag("vis ", null, r.setAdj, VISIBLE_APP_ADJ); + } else if (r.setAdj >= FOREGROUND_APP_ADJ) { + oomAdj = buildOomTag("fore ", null, r.setAdj, FOREGROUND_APP_ADJ); + } else if (r.setAdj >= CORE_SERVER_ADJ) { + oomAdj = buildOomTag("core ", null, r.setAdj, CORE_SERVER_ADJ); + } else if (r.setAdj >= SYSTEM_ADJ) { + oomAdj = buildOomTag("sys ", null, r.setAdj, SYSTEM_ADJ); + } else { + oomAdj = Integer.toString(r.setAdj); + } + String schedGroup; + switch (r.setSchedGroup) { + case Process.THREAD_GROUP_BG_NONINTERACTIVE: + schedGroup = "B"; + break; + case Process.THREAD_GROUP_DEFAULT: + schedGroup = "F"; + break; + default: + schedGroup = Integer.toString(r.setSchedGroup); + break; + } + pw.println(String.format("%s%s #%2d: adj=%s/%s %s (%s)", + prefix, (r.persistent ? persistentLabel : normalLabel), + N-i, oomAdj, schedGroup, r.toShortString(), r.adjType)); + if (r.adjSource != null || r.adjTarget != null) { + pw.print(prefix); + pw.print(" "); + if (r.adjTarget instanceof ComponentName) { + pw.print(((ComponentName)r.adjTarget).flattenToShortString()); + } else if (r.adjTarget != null) { + pw.print(r.adjTarget.toString()); } else { - oomAdj = Integer.toString(r.setAdj); - } - String schedGroup; - switch (r.setSchedGroup) { - case Process.THREAD_GROUP_BG_NONINTERACTIVE: - schedGroup = "B"; - break; - case Process.THREAD_GROUP_DEFAULT: - schedGroup = "F"; - break; - default: - schedGroup = Integer.toString(r.setSchedGroup); - break; + pw.print("{null}"); + } + pw.print("<="); + if (r.adjSource instanceof ProcessRecord) { + pw.print("Proc{"); + pw.print(((ProcessRecord)r.adjSource).toShortString()); + pw.println("}"); + } else if (r.adjSource != null) { + pw.println(r.adjSource.toString()); + } else { + pw.println("{null}"); } - pw.println(String.format("%s%s #%2d: adj=%s/%s %s (%s)", - prefix, (r.persistent ? persistentLabel : normalLabel), - N-i, oomAdj, schedGroup, r.toShortString(), r.adjType)); - if (r.adjSource != null || r.adjTarget != null) { - pw.print(prefix); - pw.print(" "); - if (r.adjTarget instanceof ComponentName) { - pw.print(((ComponentName)r.adjTarget).flattenToShortString()); - } else if (r.adjTarget != null) { - pw.print(r.adjTarget.toString()); - } else { - pw.print("{null}"); - } - pw.print("<="); - if (r.adjSource instanceof ProcessRecord) { - pw.print("Proc{"); - pw.print(((ProcessRecord)r.adjSource).toShortString()); - pw.println("}"); - } else if (r.adjSource != null) { - pw.println(r.adjSource.toString()); - } else { - pw.println("{null}"); + } + if (inclDetails) { + pw.print(prefix); + pw.print(" "); + pw.print("oom: max="); pw.print(r.maxAdj); + pw.print(" hidden="); pw.print(r.hiddenAdj); + pw.print(" curRaw="); pw.print(r.curRawAdj); + pw.print(" setRaw="); pw.print(r.setRawAdj); + pw.print(" cur="); pw.print(r.curAdj); + pw.print(" set="); pw.println(r.setAdj); + pw.print(prefix); + pw.print(" "); + pw.print("keeping="); pw.print(r.keeping); + pw.print(" hidden="); pw.print(r.hidden); + pw.print(" empty="); pw.println(r.empty); + + if (!r.keeping) { + if (r.lastWakeTime != 0) { + long wtime; + BatteryStatsImpl stats = service.mBatteryStatsService.getActiveStatistics(); + synchronized (stats) { + wtime = stats.getProcessWakeTime(r.info.uid, + r.pid, curRealtime); + } + long timeUsed = wtime - r.lastWakeTime; + pw.print(prefix); + pw.print(" "); + pw.print("keep awake over "); + TimeUtils.formatDuration(realtimeSince, pw); + pw.print(" used "); + TimeUtils.formatDuration(timeUsed, pw); + pw.print(" ("); + pw.print((timeUsed*100)/realtimeSince); + pw.println("%)"); + } + if (r.lastCpuTime != 0) { + long timeUsed = r.curCpuTime - r.lastCpuTime; + pw.print(prefix); + pw.print(" "); + pw.print("run cpu over "); + TimeUtils.formatDuration(uptimeSince, pw); + pw.print(" used "); + TimeUtils.formatDuration(timeUsed, pw); + pw.print(" ("); + pw.print((timeUsed*100)/uptimeSince); + pw.println("%)"); } } - } else { - pw.println(String.format("%s%s #%2d: %s", - prefix, (r.persistent ? persistentLabel : normalLabel), - i, r.toString())); - } - if (r.persistent) { - numPers++; } } - return numPers; } static final void dumpApplicationMemoryUsage(FileDescriptor fd, @@ -8544,7 +8705,11 @@ public final class ActivityManagerService extends ActivityManagerNative return null; } - private final void bumpServiceExecutingLocked(ServiceRecord r) { + private final void bumpServiceExecutingLocked(ServiceRecord r, String why) { + if (DEBUG_SERVICE) Log.v(TAG, ">>> EXECUTING " + + why + " of " + r + " in app " + r.app); + else if (DEBUG_SERVICE_EXECUTING) Log.v(TAG, ">>> EXECUTING " + + why + " of " + r.shortName); long now = SystemClock.uptimeMillis(); if (r.executeNesting == 0 && r.app != null) { if (r.app.executingServices.size() == 0) { @@ -8582,8 +8747,7 @@ public final class ActivityManagerService extends ActivityManagerNative grantUriPermissionUncheckedFromIntentLocked(si.targetPermissionUid, r.packageName, si.intent, si.getUriPermissionsLocked()); } - if (DEBUG_SERVICE) Slog.v(TAG, ">>> EXECUTING start of " + r); - bumpServiceExecutingLocked(r); + bumpServiceExecutingLocked(r, "start"); if (!oomAdjusted) { oomAdjusted = true; updateOomAdjLocked(r.app); @@ -8616,9 +8780,7 @@ public final class ActivityManagerService extends ActivityManagerNative } if ((!i.requested || rebind) && i.apps.size() > 0) { try { - if (DEBUG_SERVICE) Slog.v(TAG, ">>> EXECUTING bind of " + r - + " in " + i + ": shouldUnbind=" + i.hasBound); - bumpServiceExecutingLocked(r); + bumpServiceExecutingLocked(r, "bind"); r.app.thread.scheduleBindService(r, i.intent.getIntent(), rebind); if (!rebind) { i.requested = true; @@ -8653,8 +8815,7 @@ public final class ActivityManagerService extends ActivityManagerNative r.restartTime = r.lastActivity = SystemClock.uptimeMillis(); app.services.add(r); - if (DEBUG_SERVICE) Slog.v(TAG, ">>> EXECUTING create of " + r + " " + r.intent); - bumpServiceExecutingLocked(r); + bumpServiceExecutingLocked(r, "create"); updateLruProcessLocked(app, true, true); boolean created = false; @@ -8906,9 +9067,7 @@ public final class ActivityManagerService extends ActivityManagerNative + ": hasBound=" + ibr.hasBound); if (r.app != null && r.app.thread != null && ibr.hasBound) { try { - if (DEBUG_SERVICE) Slog.v(TAG, ">>> EXECUTING bring down unbind of " + r - + " for " + ibr); - bumpServiceExecutingLocked(r); + bumpServiceExecutingLocked(r, "bring down unbind"); updateOomAdjLocked(r.app); ibr.hasBound = false; r.app.thread.scheduleUnbindService(r, @@ -8959,12 +9118,7 @@ public final class ActivityManagerService extends ActivityManagerNative r.app.services.remove(r); if (r.app.thread != null) { try { - if (DEBUG_SERVICE) { - RuntimeException here = new RuntimeException(); - here.fillInStackTrace(); - Slog.v(TAG, ">>> EXECUTING stop of " + r, here); - } - bumpServiceExecutingLocked(r); + bumpServiceExecutingLocked(r, "stop"); mStoppingServices.add(r); updateOomAdjLocked(r.app); r.app.thread.scheduleStopService(r); @@ -9420,9 +9574,7 @@ public final class ActivityManagerService extends ActivityManagerNative if (s.app != null && s.app.thread != null && b.intent.apps.size() == 0 && b.intent.hasBound) { try { - if (DEBUG_SERVICE) Slog.v(TAG, ">>> EXECUTING unbind of " + s - + " from " + b); - bumpServiceExecutingLocked(s); + bumpServiceExecutingLocked(s, "unbind"); updateOomAdjLocked(s.app); b.intent.hasBound = false; // Assume the client doesn't want to know about a rebind; @@ -9643,14 +9795,20 @@ public final class ActivityManagerService extends ActivityManagerNative if (DEBUG_SERVICE) Slog.v(TAG, "<<< DONE EXECUTING " + r + ": nesting=" + r.executeNesting + ", inStopping=" + inStopping + ", app=" + r.app); + else if (DEBUG_SERVICE_EXECUTING) Slog.v(TAG, "<<< DONE EXECUTING " + r.shortName); r.executeNesting--; if (r.executeNesting <= 0 && r.app != null) { + if (DEBUG_SERVICE) Slog.v(TAG, + "Nesting at 0 of " + r.shortName); r.app.executingServices.remove(r); if (r.app.executingServices.size() == 0) { + if (DEBUG_SERVICE || DEBUG_SERVICE_EXECUTING) Slog.v(TAG, + "No more executingServices of " + r.shortName); mHandler.removeMessages(SERVICE_TIMEOUT_MSG, r.app); } if (inStopping) { - if (DEBUG_SERVICE) Slog.v(TAG, "doneExecuting remove stopping " + r); + if (DEBUG_SERVICE) Slog.v(TAG, + "doneExecuting remove stopping " + r); mStoppingServices.remove(r); } updateOomAdjLocked(r.app); @@ -9760,20 +9918,20 @@ public final class ActivityManagerService extends ActivityManagerNative Slog.e(TAG, "Backup agent created for " + agentPackageName + " but not requested!"); return; } + } - long oldIdent = Binder.clearCallingIdentity(); - try { - IBackupManager bm = IBackupManager.Stub.asInterface( - ServiceManager.getService(Context.BACKUP_SERVICE)); - bm.agentConnected(agentPackageName, agent); - } catch (RemoteException e) { - // can't happen; the backup manager service is local - } catch (Exception e) { - Slog.w(TAG, "Exception trying to deliver BackupAgent binding: "); - e.printStackTrace(); - } finally { - Binder.restoreCallingIdentity(oldIdent); - } + long oldIdent = Binder.clearCallingIdentity(); + try { + IBackupManager bm = IBackupManager.Stub.asInterface( + ServiceManager.getService(Context.BACKUP_SERVICE)); + bm.agentConnected(agentPackageName, agent); + } catch (RemoteException e) { + // can't happen; the backup manager service is local + } catch (Exception e) { + Slog.w(TAG, "Exception trying to deliver BackupAgent binding: "); + e.printStackTrace(); + } finally { + Binder.restoreCallingIdentity(oldIdent); } } @@ -10486,7 +10644,7 @@ public final class ActivityManagerService extends ActivityManagerNative Binder.restoreCallingIdentity(origId); } - private final void logBroadcastReceiverDiscard(BroadcastRecord r) { + private final void logBroadcastReceiverDiscardLocked(BroadcastRecord r) { if (r.nextReceiver > 0) { Object curReceiver = r.receivers.get(r.nextReceiver-1); if (curReceiver instanceof BroadcastFilter) { @@ -10514,67 +10672,108 @@ public final class ActivityManagerService extends ActivityManagerNative } } - private final void broadcastTimeout() { - ProcessRecord app = null; - String anrMessage = null; + private final void setBroadcastTimeoutLocked(long timeoutTime) { + if (! mPendingBroadcastTimeoutMessage) { + Message msg = mHandler.obtainMessage(BROADCAST_TIMEOUT_MSG); + mHandler.sendMessageAtTime(msg, timeoutTime); + mPendingBroadcastTimeoutMessage = true; + } + } - synchronized (this) { - if (mOrderedBroadcasts.size() == 0) { + private final void cancelBroadcastTimeoutLocked() { + if (mPendingBroadcastTimeoutMessage) { + mHandler.removeMessages(BROADCAST_TIMEOUT_MSG); + mPendingBroadcastTimeoutMessage = false; + } + } + + private final void broadcastTimeoutLocked(boolean fromMsg) { + if (fromMsg) { + mPendingBroadcastTimeoutMessage = false; + } + + if (mOrderedBroadcasts.size() == 0) { + return; + } + + long now = SystemClock.uptimeMillis(); + BroadcastRecord r = mOrderedBroadcasts.get(0); + if (fromMsg) { + if (mDidDexOpt) { + // Delay timeouts until dexopt finishes. + mDidDexOpt = false; + long timeoutTime = SystemClock.uptimeMillis() + BROADCAST_TIMEOUT; + setBroadcastTimeoutLocked(timeoutTime); return; } - long now = SystemClock.uptimeMillis(); - BroadcastRecord r = mOrderedBroadcasts.get(0); - if ((r.receiverTime+BROADCAST_TIMEOUT) > now) { + if (! mProcessesReady) { + // Only process broadcast timeouts if the system is ready. That way + // PRE_BOOT_COMPLETED broadcasts can't timeout as they are intended + // to do heavy lifting for system up. + return; + } + + long timeoutTime = r.receiverTime + BROADCAST_TIMEOUT; + if (timeoutTime > now) { + // We can observe premature timeouts because we do not cancel and reset the + // broadcast timeout message after each receiver finishes. Instead, we set up + // an initial timeout then kick it down the road a little further as needed + // when it expires. if (DEBUG_BROADCAST) Slog.v(TAG, "Premature timeout @ " + now + ": resetting BROADCAST_TIMEOUT_MSG for " - + (r.receiverTime + BROADCAST_TIMEOUT)); - Message msg = mHandler.obtainMessage(BROADCAST_TIMEOUT_MSG); - mHandler.sendMessageAtTime(msg, r.receiverTime+BROADCAST_TIMEOUT); + + timeoutTime); + setBroadcastTimeoutLocked(timeoutTime); return; } + } - Slog.w(TAG, "Timeout of broadcast " + r + " - receiver=" + r.receiver); - r.receiverTime = now; - r.anrCount++; + Slog.w(TAG, "Timeout of broadcast " + r + " - receiver=" + r.receiver + + ", started " + (now - r.receiverTime) + "ms ago"); + r.receiverTime = now; + r.anrCount++; - // Current receiver has passed its expiration date. - if (r.nextReceiver <= 0) { - Slog.w(TAG, "Timeout on receiver with nextReceiver <= 0"); - return; - } + // Current receiver has passed its expiration date. + if (r.nextReceiver <= 0) { + Slog.w(TAG, "Timeout on receiver with nextReceiver <= 0"); + return; + } - Object curReceiver = r.receivers.get(r.nextReceiver-1); - Slog.w(TAG, "Receiver during timeout: " + curReceiver); - logBroadcastReceiverDiscard(r); - if (curReceiver instanceof BroadcastFilter) { - BroadcastFilter bf = (BroadcastFilter)curReceiver; - if (bf.receiverList.pid != 0 - && bf.receiverList.pid != MY_PID) { - synchronized (this.mPidsSelfLocked) { - app = this.mPidsSelfLocked.get( - bf.receiverList.pid); - } - } - } else { - app = r.curApp; - } - - if (app != null) { - anrMessage = "Broadcast of " + r.intent.toString(); - } + ProcessRecord app = null; + String anrMessage = null; - if (mPendingBroadcast == r) { - mPendingBroadcast = null; + Object curReceiver = r.receivers.get(r.nextReceiver-1); + Slog.w(TAG, "Receiver during timeout: " + curReceiver); + logBroadcastReceiverDiscardLocked(r); + if (curReceiver instanceof BroadcastFilter) { + BroadcastFilter bf = (BroadcastFilter)curReceiver; + if (bf.receiverList.pid != 0 + && bf.receiverList.pid != MY_PID) { + synchronized (this.mPidsSelfLocked) { + app = this.mPidsSelfLocked.get( + bf.receiverList.pid); + } } - - // Move on to the next receiver. - finishReceiverLocked(r.receiver, r.resultCode, r.resultData, - r.resultExtras, r.resultAbort, true); - scheduleBroadcastsLocked(); + } else { + app = r.curApp; } + if (app != null) { + anrMessage = "Broadcast of " + r.intent.toString(); + } + + if (mPendingBroadcast == r) { + mPendingBroadcast = null; + } + + // Move on to the next receiver. + finishReceiverLocked(r.receiver, r.resultCode, r.resultData, + r.resultExtras, r.resultAbort, true); + scheduleBroadcastsLocked(); + if (anrMessage != null) { - appNotResponding(app, null, null, anrMessage); + // Post the ANR to the handler since we do not want to process ANRs while + // potentially holding our lock. + mHandler.post(new AppNotResponding(app, anrMessage)); } } @@ -10616,9 +10815,10 @@ public final class ActivityManagerService extends ActivityManagerNative } - static void performReceive(ProcessRecord app, IIntentReceiver receiver, + static void performReceiveLocked(ProcessRecord app, IIntentReceiver receiver, Intent intent, int resultCode, String data, Bundle extras, boolean ordered, boolean sticky) throws RemoteException { + // Send the intent to the receiver asynchronously using one-way binder calls. if (app != null && app.thread != null) { // If we have an app thread, do the call through that so it is // correctly ordered with other one-way calls. @@ -10629,7 +10829,7 @@ public final class ActivityManagerService extends ActivityManagerNative } } - private final void deliverToRegisteredReceiver(BroadcastRecord r, + private final void deliverToRegisteredReceiverLocked(BroadcastRecord r, BroadcastFilter filter, boolean ordered) { boolean skip = false; if (filter.requiredPermission != null) { @@ -10687,7 +10887,7 @@ public final class ActivityManagerService extends ActivityManagerNative Slog.i(TAG, "Delivering to " + filter + " (seq=" + seq + "): " + r); } - performReceive(filter.receiverList.app, filter.receiverList.receiver, + performReceiveLocked(filter.receiverList.app, filter.receiverList.receiver, new Intent(r.intent), r.resultCode, r.resultData, r.resultExtras, r.ordered, r.initialSticky); if (ordered) { @@ -10744,7 +10944,7 @@ public final class ActivityManagerService extends ActivityManagerNative if (DEBUG_BROADCAST) Slog.v(TAG, "Delivering non-ordered to registered " + target + ": " + r); - deliverToRegisteredReceiver(r, (BroadcastFilter)target, false); + deliverToRegisteredReceiverLocked(r, (BroadcastFilter)target, false); } addBroadcastToHistoryLocked(r); if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG, "Done with parallel broadcast " @@ -10800,7 +11000,7 @@ public final class ActivityManagerService extends ActivityManagerNative // and continue to make progress. // // This is only done if the system is ready so that PRE_BOOT_COMPLETED - // receivers don't get executed with with timeouts. They're intended for + // receivers don't get executed with timeouts. They're intended for // one time heavy lifting after system upgrades and can take // significant amounts of time. int numReceivers = (r.receivers != null) ? r.receivers.size() : 0; @@ -10816,7 +11016,7 @@ public final class ActivityManagerService extends ActivityManagerNative + " numReceivers=" + numReceivers + " nextReceiver=" + r.nextReceiver + " state=" + r.state); - broadcastTimeout(); // forcibly finish this broadcast + broadcastTimeoutLocked(false); // forcibly finish this broadcast forceReceive = true; r.state = BroadcastRecord.IDLE; } @@ -10840,7 +11040,7 @@ public final class ActivityManagerService extends ActivityManagerNative Slog.i(TAG, "Finishing broadcast " + r.intent.getAction() + " seq=" + seq + " app=" + r.callerApp); } - performReceive(r.callerApp, r.resultTo, + performReceiveLocked(r.callerApp, r.resultTo, new Intent(r.intent), r.resultCode, r.resultData, r.resultExtras, false, false); } catch (RemoteException e) { @@ -10849,7 +11049,7 @@ public final class ActivityManagerService extends ActivityManagerNative } if (DEBUG_BROADCAST) Slog.v(TAG, "Cancelling BROADCAST_TIMEOUT_MSG"); - mHandler.removeMessages(BROADCAST_TIMEOUT_MSG); + cancelBroadcastTimeoutLocked(); if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG, "Finished with ordered broadcast " + r); @@ -10874,11 +11074,12 @@ public final class ActivityManagerService extends ActivityManagerNative if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG, "Processing ordered broadcast " + r); + } + if (! mPendingBroadcastTimeoutMessage) { + long timeoutTime = r.receiverTime + BROADCAST_TIMEOUT; if (DEBUG_BROADCAST) Slog.v(TAG, - "Submitting BROADCAST_TIMEOUT_MSG for " + r + " at " - + (r.receiverTime + BROADCAST_TIMEOUT)); - Message msg = mHandler.obtainMessage(BROADCAST_TIMEOUT_MSG); - mHandler.sendMessageAtTime(msg, r.receiverTime+BROADCAST_TIMEOUT); + "Submitting BROADCAST_TIMEOUT_MSG for " + r + " at " + timeoutTime); + setBroadcastTimeoutLocked(timeoutTime); } Object nextReceiver = r.receivers.get(recIdx); @@ -10889,7 +11090,7 @@ public final class ActivityManagerService extends ActivityManagerNative if (DEBUG_BROADCAST) Slog.v(TAG, "Delivering ordered to registered " + filter + ": " + r); - deliverToRegisteredReceiver(r, filter, r.ordered); + deliverToRegisteredReceiverLocked(r, filter, r.ordered); if (r.receiver == null || !r.ordered) { // The receiver has already finished, so schedule to // process the next one. @@ -10997,7 +11198,7 @@ public final class ActivityManagerService extends ActivityManagerNative + info.activityInfo.applicationInfo.packageName + "/" + info.activityInfo.applicationInfo.uid + " for broadcast " + r.intent + ": process is bad"); - logBroadcastReceiverDiscard(r); + logBroadcastReceiverDiscardLocked(r); finishReceiverLocked(r.receiver, r.resultCode, r.resultData, r.resultExtras, r.resultAbort, true); scheduleBroadcastsLocked(); @@ -11326,6 +11527,7 @@ public final class ActivityManagerService extends ActivityManagerNative app.adjType = "fixed"; app.adjSeq = mAdjSeq; app.curRawAdj = app.maxAdj; + app.keeping = true; app.curSchedGroup = Process.THREAD_GROUP_DEFAULT; return (app.curAdj=app.maxAdj); } @@ -11333,6 +11535,7 @@ public final class ActivityManagerService extends ActivityManagerNative app.adjTypeCode = ActivityManager.RunningAppProcessInfo.REASON_UNKNOWN; app.adjSource = null; app.adjTarget = null; + app.keeping = false; app.empty = false; app.hidden = false; @@ -11462,6 +11665,9 @@ public final class ActivityManagerService extends ActivityManagerNative if (adj > SECONDARY_SERVER_ADJ) { app.adjType = "started-bg-services"; } + // Don't kill this process because it is doing work; it + // has said it is doing work. + app.keeping = true; } if (s.connections.size() > 0 && (adj > FOREGROUND_APP_ADJ || schedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE)) { @@ -11495,6 +11701,9 @@ public final class ActivityManagerService extends ActivityManagerNative if (!client.hidden) { app.hidden = false; } + if (client.keeping) { + app.keeping = true; + } app.adjType = "service"; app.adjTypeCode = ActivityManager.RunningAppProcessInfo .REASON_SERVICE_IN_USE; @@ -11528,7 +11737,7 @@ public final class ActivityManagerService extends ActivityManagerNative } } - // Finally, f this process has active services running in it, we + // Finally, if this process has active services running in it, we // would like to avoid killing it unless it would prevent the current // application from running. By default we put the process in // with the rest of the background processes; as we scan through @@ -11570,6 +11779,9 @@ public final class ActivityManagerService extends ActivityManagerNative if (!client.hidden) { app.hidden = false; } + if (client.keeping) { + app.keeping = true; + } app.adjType = "provider"; app.adjTypeCode = ActivityManager.RunningAppProcessInfo .REASON_PROVIDER_IN_USE; @@ -11589,6 +11801,7 @@ public final class ActivityManagerService extends ActivityManagerNative adj = FOREGROUND_APP_ADJ; schedGroup = Process.THREAD_GROUP_DEFAULT; app.hidden = false; + app.keeping = true; app.adjType = "provider"; app.adjTarget = cpr.name; } @@ -11606,6 +11819,9 @@ public final class ActivityManagerService extends ActivityManagerNative schedGroup = Process.THREAD_GROUP_DEFAULT; } } + if (adj < HIDDEN_APP_MIN_ADJ) { + app.keeping = true; + } app.curAdj = adj; app.curSchedGroup = schedGroup; @@ -11743,57 +11959,99 @@ public final class ActivityManagerService extends ActivityManagerNative } } - final void checkExcessiveWakeLocksLocked(boolean doKills) { + final void checkExcessivePowerUsageLocked(boolean doKills) { + updateCpuStatsNow(); + BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics(); - if (mLastWakeLockCheckTime == 0) { - doKills = false; + boolean doWakeKills = doKills; + boolean doCpuKills = doKills; + if (mLastPowerCheckRealtime == 0) { + doWakeKills = false; + } + if (mLastPowerCheckUptime == 0) { + doCpuKills = false; } if (stats.isScreenOn()) { - doKills = false; + doWakeKills = false; } final long curRealtime = SystemClock.elapsedRealtime(); - final long timeSince = curRealtime - mLastWakeLockCheckTime; - mLastWakeLockCheckTime = curRealtime; - if (timeSince < (WAKE_LOCK_CHECK_DELAY/3)) { - doKills = false; + final long realtimeSince = curRealtime - mLastPowerCheckRealtime; + final long curUptime = SystemClock.uptimeMillis(); + final long uptimeSince = curUptime - mLastPowerCheckUptime; + mLastPowerCheckRealtime = curRealtime; + mLastPowerCheckUptime = curUptime; + if (realtimeSince < WAKE_LOCK_MIN_CHECK_DURATION) { + doWakeKills = false; + } + if (uptimeSince < CPU_MIN_CHECK_DURATION) { + doCpuKills = false; } int i = mLruProcesses.size(); while (i > 0) { i--; ProcessRecord app = mLruProcesses.get(i); - if (app.curAdj >= HIDDEN_APP_MIN_ADJ) { + if (!app.keeping) { long wtime; synchronized (stats) { wtime = stats.getProcessWakeTime(app.info.uid, app.pid, curRealtime); } - long timeUsed = wtime - app.lastWakeTime; - if (false) { + long wtimeUsed = wtime - app.lastWakeTime; + long cputimeUsed = app.curCpuTime - app.lastCpuTime; + if (DEBUG_POWER) { StringBuilder sb = new StringBuilder(128); sb.append("Wake for "); app.toShortString(sb); sb.append(": over "); - TimeUtils.formatDuration(timeSince, sb); + TimeUtils.formatDuration(realtimeSince, sb); + sb.append(" used "); + TimeUtils.formatDuration(wtimeUsed, sb); + sb.append(" ("); + sb.append((wtimeUsed*100)/realtimeSince); + sb.append("%)"); + Slog.i(TAG, sb.toString()); + sb.setLength(0); + sb.append("CPU for "); + app.toShortString(sb); + sb.append(": over "); + TimeUtils.formatDuration(uptimeSince, sb); sb.append(" used "); - TimeUtils.formatDuration(timeUsed, sb); + TimeUtils.formatDuration(cputimeUsed, sb); sb.append(" ("); - sb.append((timeUsed*100)/timeSince); + sb.append((cputimeUsed*100)/uptimeSince); sb.append("%)"); Slog.i(TAG, sb.toString()); } // If a process has held a wake lock for more // than 50% of the time during this period, // that sounds pad. Kill! - if (doKills && timeSince > 0 - && ((timeUsed*100)/timeSince) >= 50) { - Slog.i(TAG, "Excessive wake lock in " + app.processName - + " (pid " + app.pid + "): held " + timeUsed - + " during " + timeSince); + if (doWakeKills && realtimeSince > 0 + && ((wtimeUsed*100)/realtimeSince) >= 50) { + synchronized (stats) { + stats.reportExcessiveWakeLocked(app.info.uid, app.processName, + realtimeSince, wtimeUsed); + } + Slog.w(TAG, "Excessive wake lock in " + app.processName + + " (pid " + app.pid + "): held " + wtimeUsed + + " during " + realtimeSince); EventLog.writeEvent(EventLogTags.AM_KILL, app.pid, app.processName, app.setAdj, "excessive wake lock"); Process.killProcessQuiet(app.pid); + } else if (doCpuKills && uptimeSince > 0 + && ((cputimeUsed*100)/uptimeSince) >= 50) { + synchronized (stats) { + stats.reportExcessiveCpuLocked(app.info.uid, app.processName, + uptimeSince, cputimeUsed); + } + Slog.w(TAG, "Excessive CPU in " + app.processName + + " (pid " + app.pid + "): used " + cputimeUsed + + " during " + uptimeSince); + EventLog.writeEvent(EventLogTags.AM_KILL, app.pid, + app.processName, app.setAdj, "excessive cpu"); + Process.killProcessQuiet(app.pid); } else { app.lastWakeTime = wtime; + app.lastCpuTime = app.curCpuTime; } } } @@ -11807,6 +12065,8 @@ public final class ActivityManagerService extends ActivityManagerNative return true; } + final boolean wasKeeping = app.keeping; + int adj = computeOomAdjLocked(app, hiddenAdj, TOP_APP, false); if ((app.pid != 0 && app.pid != MY_PID) || Process.supportsProcesses()) { @@ -11821,13 +12081,20 @@ public final class ActivityManagerService extends ActivityManagerNative // Likewise do a gc when an app is moving in to the // background (such as a service stopping). scheduleAppGcLocked(app); - // And note its current wake lock time. + } + + if (wasKeeping && !app.keeping) { + // This app is no longer something we want to keep. Note + // its current wake lock time to later know to kill it if + // it is not behaving well. BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics(); synchronized (stats) { app.lastWakeTime = stats.getProcessWakeTime(app.info.uid, app.pid, SystemClock.elapsedRealtime()); } + app.lastCpuTime = app.curCpuTime; } + app.setRawAdj = app.curRawAdj; } if (adj != app.setAdj) { diff --git a/services/java/com/android/server/am/ProcessRecord.java b/services/java/com/android/server/am/ProcessRecord.java index 67df707..404c6be 100644 --- a/services/java/com/android/server/am/ProcessRecord.java +++ b/services/java/com/android/server/am/ProcessRecord.java @@ -60,6 +60,7 @@ class ProcessRecord { int setAdj; // Last set OOM adjustment for this process int curSchedGroup; // Currently desired scheduling class int setSchedGroup; // Last set to background scheduling class + boolean keeping; // Actively running code so don't kill due to that? boolean setIsForeground; // Running foreground UI when last set? boolean foregroundServices; // Running any services that are foreground? boolean bad; // True if disabled in the bad process list @@ -75,6 +76,8 @@ class ProcessRecord { ComponentName instrumentationResultClass;// copy of instrumentationClass BroadcastRecord curReceiver;// receiver currently running in the app long lastWakeTime; // How long proc held wake lock at last check + long lastCpuTime; // How long proc has run CPU at last check + long curCpuTime; // How long proc has run CPU most recently long lastRequestedGc; // When we last asked the app to do a gc long lastLowMemory; // When we last told the app that memory is low boolean reportLowMemory; // Set to true when waiting to report low mem @@ -131,13 +134,6 @@ class ProcessRecord { void dump(PrintWriter pw, String prefix) { final long now = SystemClock.uptimeMillis(); - long wtime; - synchronized (batteryStats.getBatteryStats()) { - wtime = batteryStats.getBatteryStats().getProcessWakeTime(info.uid, - pid, SystemClock.elapsedRealtime()); - } - long timeUsed = wtime - lastWakeTime; - if (info.className != null) { pw.print(prefix); pw.print("class="); pw.println(info.className); } @@ -170,6 +166,7 @@ class ProcessRecord { pw.print(prefix); pw.print("lastActivityTime="); TimeUtils.formatDuration(lastActivityTime, now, pw); pw.print(" lruWeight="); pw.print(lruWeight); + pw.print(" keeping="); pw.print(keeping); pw.print(" hidden="); pw.print(hidden); pw.print(" empty="); pw.println(empty); pw.print(prefix); pw.print("oom: max="); pw.print(maxAdj); @@ -188,9 +185,20 @@ class ProcessRecord { pw.print(" persistentActivities="); pw.println(persistentActivities); pw.print(prefix); pw.print("adjSeq="); pw.print(adjSeq); pw.print(" lruSeq="); pw.println(lruSeq); - pw.print(prefix); pw.print("lastWakeTime="); pw.print(lastWakeTime); - pw.print(" time used="); - TimeUtils.formatDuration(timeUsed, pw); pw.println(""); + if (!keeping) { + long wtime; + synchronized (batteryStats.getBatteryStats()) { + wtime = batteryStats.getBatteryStats().getProcessWakeTime(info.uid, + pid, SystemClock.elapsedRealtime()); + } + long timeUsed = wtime - lastWakeTime; + pw.print(prefix); pw.print("lastWakeTime="); pw.print(lastWakeTime); + pw.print(" time used="); + TimeUtils.formatDuration(timeUsed, pw); pw.println(""); + pw.print(prefix); pw.print("lastCpuTime="); pw.print(lastCpuTime); + pw.print(" time used="); + TimeUtils.formatDuration(curCpuTime-lastCpuTime, pw); pw.println(""); + } pw.print(prefix); pw.print("lastRequestedGc="); TimeUtils.formatDuration(lastRequestedGc, now, pw); pw.print(" lastLowMemory="); diff --git a/services/java/com/android/server/am/ServiceRecord.java b/services/java/com/android/server/am/ServiceRecord.java index f35a68e..e5aceb4 100644 --- a/services/java/com/android/server/am/ServiceRecord.java +++ b/services/java/com/android/server/am/ServiceRecord.java @@ -347,7 +347,9 @@ class ServiceRecord extends Binder { // If it gave us a garbage notification, it doesn't // get to be foreground. ams.setServiceForeground(name, ServiceRecord.this, - localForegroundId, null, true); + 0, null, true); + ams.crashApplication(appUid, appPid, localPackageName, + "Bad notification for startForeground: " + e); } } }); diff --git a/services/java/com/android/server/sip/SipService.java b/services/java/com/android/server/sip/SipService.java index f1dcd5a..3f43e1c 100644 --- a/services/java/com/android/server/sip/SipService.java +++ b/services/java/com/android/server/sip/SipService.java @@ -30,8 +30,8 @@ import android.net.sip.ISipSessionListener; import android.net.sip.SipErrorCode; import android.net.sip.SipManager; import android.net.sip.SipProfile; +import android.net.sip.SipSession; import android.net.sip.SipSessionAdapter; -import android.net.sip.SipSessionState; import android.net.wifi.WifiManager; import android.os.Binder; import android.os.Bundle; @@ -143,7 +143,7 @@ public final class SipService extends ISipService.Stub { } private void openToReceiveCalls(SipProfile localProfile) { - open3(localProfile, SipManager.SIP_INCOMING_CALL_ACTION, null); + open3(localProfile, SipManager.ACTION_SIP_INCOMING_CALL, null); } public synchronized void open3(SipProfile localProfile, @@ -255,15 +255,15 @@ public final class SipService extends ISipService.Stub { private void notifyProfileAdded(SipProfile localProfile) { if (DEBUG) Log.d(TAG, "notify: profile added: " + localProfile); - Intent intent = new Intent(SipManager.SIP_ADD_PHONE_ACTION); - intent.putExtra(SipManager.LOCAL_URI_KEY, localProfile.getUriString()); + Intent intent = new Intent(SipManager.ACTION_SIP_ADD_PHONE); + intent.putExtra(SipManager.EXTRA_LOCAL_URI, localProfile.getUriString()); mContext.sendBroadcast(intent); } private void notifyProfileRemoved(SipProfile localProfile) { if (DEBUG) Log.d(TAG, "notify: profile removed: " + localProfile); - Intent intent = new Intent(SipManager.SIP_REMOVE_PHONE_ACTION); - intent.putExtra(SipManager.LOCAL_URI_KEY, localProfile.getUriString()); + Intent intent = new Intent(SipManager.ACTION_SIP_REMOVE_PHONE); + intent.putExtra(SipManager.EXTRA_LOCAL_URI, localProfile.getUriString()); mContext.sendBroadcast(intent); } @@ -474,8 +474,8 @@ public final class SipService extends ISipService.Stub { // send out incoming call broadcast addPendingSession(session); Intent intent = SipManager.createIncomingCallBroadcast( - mIncomingCallBroadcastAction, session.getCallId(), - sessionDescription); + session.getCallId(), sessionDescription) + .setAction(mIncomingCallBroadcastAction); if (DEBUG) Log.d(TAG, " ringing~~ " + getUri() + ": " + caller.getUri() + ": " + session.getCallId() + " " + mIncomingCallBroadcastAction); @@ -613,10 +613,10 @@ public final class SipService extends ISipService.Stub { try { int state = (mSession == null) - ? SipSessionState.READY_TO_CALL + ? SipSession.State.READY_TO_CALL : mSession.getState(); - if ((state == SipSessionState.REGISTERING) - || (state == SipSessionState.DEREGISTERING)) { + if ((state == SipSession.State.REGISTERING) + || (state == SipSession.State.DEREGISTERING)) { mProxy.onRegistering(mSession); } else if (mRegistered) { int duration = (int) @@ -1138,7 +1138,8 @@ public final class SipService extends ISipService.Stub { event.mTriggerTime += event.mPeriod; // run the callback in a new thread to prevent deadlock - new Thread(event.mCallback).start(); + new Thread(event.mCallback, "SipServiceTimerCallbackThread") + .start(); } if (DEBUG_TIMER) { Log.d(TAG, "after timeout execution"); diff --git a/services/java/com/android/server/sip/SipSessionGroup.java b/services/java/com/android/server/sip/SipSessionGroup.java index b4c2241..fa3f64a 100644 --- a/services/java/com/android/server/sip/SipSessionGroup.java +++ b/services/java/com/android/server/sip/SipSessionGroup.java @@ -25,11 +25,10 @@ import gov.nist.javax.sip.message.SIPMessage; import android.net.sip.ISipSession; import android.net.sip.ISipSessionListener; -import android.net.sip.SessionDescription; import android.net.sip.SipErrorCode; import android.net.sip.SipProfile; +import android.net.sip.SipSession; import android.net.sip.SipSessionAdapter; -import android.net.sip.SipSessionState; import android.text.TextUtils; import android.util.Log; @@ -85,7 +84,7 @@ class SipSessionGroup implements SipListener { private static final String ANONYMOUS = "anonymous"; private static final String SERVER_ERROR_PREFIX = "Response: "; private static final int EXPIRY_TIME = 3600; // in seconds - private static final int CANCEL_CALL_TIMER = 5; // in seconds + private static final int CANCEL_CALL_TIMER = 3; // in seconds private static final EventObject DEREGISTER = new EventObject("Deregister"); private static final EventObject END_CALL = new EventObject("End call"); @@ -121,7 +120,7 @@ class SipSessionGroup implements SipListener { reset(localIp); } - void reset(String localIp) throws SipException, IOException { + synchronized void reset(String localIp) throws SipException, IOException { mLocalIp = localIp; if (localIp == null) return; @@ -301,7 +300,7 @@ class SipSessionGroup implements SipListener { boolean processed = (session != null) && session.process(event); if (isLoggable && processed) { Log.d(TAG, "new state after: " - + SipSessionState.toString(session.mState)); + + SipSession.State.toString(session.mState)); } } catch (Throwable e) { Log.w(TAG, "event process error: " + event, e); @@ -332,7 +331,7 @@ class SipSessionGroup implements SipListener { public boolean process(EventObject evt) throws SipException { if (isLoggable(this, evt)) Log.d(TAG, " ~~~~~ " + this + ": " - + SipSessionState.toString(mState) + ": processing " + + SipSession.State.toString(mState) + ": processing " + log(evt)); if (isRequestEvent(Request.INVITE, evt)) { RequestEvent event = (RequestEvent) evt; @@ -342,13 +341,16 @@ class SipSessionGroup implements SipListener { newSession.mDialog = newSession.mServerTransaction.getDialog(); newSession.mInviteReceived = event; newSession.mPeerProfile = createPeerProfile(event.getRequest()); - newSession.mState = SipSessionState.INCOMING_CALL; + newSession.mState = SipSession.State.INCOMING_CALL; newSession.mPeerSessionDescription = extractContent(event.getRequest()); addSipSession(newSession); mProxy.onRinging(newSession, newSession.mPeerProfile, newSession.mPeerSessionDescription); return true; + } else if (isRequestEvent(Request.OPTIONS, evt)) { + mSipHelper.sendResponse((RequestEvent) evt, Response.OK); + return true; } else { return false; } @@ -358,7 +360,7 @@ class SipSessionGroup implements SipListener { class SipSessionImpl extends ISipSession.Stub { SipProfile mPeerProfile; SipSessionListenerProxy mProxy = new SipSessionListenerProxy(); - int mState = SipSessionState.READY_TO_CALL; + int mState = SipSession.State.READY_TO_CALL; RequestEvent mInviteReceived; Dialog mDialog; ServerTransaction mServerTransaction; @@ -378,7 +380,7 @@ class SipSessionGroup implements SipListener { sleep(timeout); if (mRunning) timeout(); } - }).start(); + }, "SipSessionTimerThread").start(); } synchronized void cancel() { @@ -413,7 +415,7 @@ class SipSessionGroup implements SipListener { mInCall = false; removeSipSession(this); mPeerProfile = null; - mState = SipSessionState.READY_TO_CALL; + mState = SipSession.State.READY_TO_CALL; mInviteReceived = null; mDialog = null; mServerTransaction = null; @@ -470,7 +472,7 @@ class SipSessionGroup implements SipListener { onError(e); } } - }).start(); + }, "SipSessionAsyncCmdThread").start(); } public void makeCall(SipProfile peerProfile, String sessionDescription, @@ -520,10 +522,10 @@ class SipSessionGroup implements SipListener { } public void sendKeepAlive() { - mState = SipSessionState.PINGING; + mState = SipSession.State.PINGING; try { processCommand(new OptionsCommand()); - while (SipSessionState.PINGING == mState) { + while (SipSession.State.PINGING == mState) { Thread.sleep(1000); } } catch (SipException e) { @@ -550,7 +552,7 @@ class SipSessionGroup implements SipListener { try { String s = super.toString(); return s.substring(s.indexOf("@")) + ":" - + SipSessionState.toString(mState); + + SipSession.State.toString(mState); } catch (Throwable e) { return super.toString(); } @@ -558,7 +560,7 @@ class SipSessionGroup implements SipListener { public boolean process(EventObject evt) throws SipException { if (isLoggable(this, evt)) Log.d(TAG, " ~~~~~ " + this + ": " - + SipSessionState.toString(mState) + ": processing " + + SipSession.State.toString(mState) + ": processing " + log(evt)); synchronized (SipSessionGroup.this) { if (isClosed()) return false; @@ -574,30 +576,30 @@ class SipSessionGroup implements SipListener { boolean processed; switch (mState) { - case SipSessionState.REGISTERING: - case SipSessionState.DEREGISTERING: + case SipSession.State.REGISTERING: + case SipSession.State.DEREGISTERING: processed = registeringToReady(evt); break; - case SipSessionState.PINGING: + case SipSession.State.PINGING: processed = keepAliveProcess(evt); break; - case SipSessionState.READY_TO_CALL: + case SipSession.State.READY_TO_CALL: processed = readyForCall(evt); break; - case SipSessionState.INCOMING_CALL: + case SipSession.State.INCOMING_CALL: processed = incomingCall(evt); break; - case SipSessionState.INCOMING_CALL_ANSWERING: + case SipSession.State.INCOMING_CALL_ANSWERING: processed = incomingCallToInCall(evt); break; - case SipSessionState.OUTGOING_CALL: - case SipSessionState.OUTGOING_CALL_RING_BACK: + case SipSession.State.OUTGOING_CALL: + case SipSession.State.OUTGOING_CALL_RING_BACK: processed = outgoingCall(evt); break; - case SipSessionState.OUTGOING_CALL_CANCELING: + case SipSession.State.OUTGOING_CALL_CANCELING: processed = outgoingCallToReady(evt); break; - case SipSessionState.IN_CALL: + case SipSession.State.IN_CALL: processed = inCall(evt); break; default: @@ -625,6 +627,9 @@ class SipSessionGroup implements SipListener { (TransactionTerminatedEvent) evt); } return true; + } else if (isRequestEvent(Request.OPTIONS, evt)) { + mSipHelper.sendResponse((RequestEvent) evt, Response.OK); + return true; } else if (evt instanceof DialogTerminatedEvent) { processDialogTerminated((DialogTerminatedEvent) evt); return true; @@ -644,8 +649,8 @@ class SipSessionGroup implements SipListener { private void processTransactionTerminated( TransactionTerminatedEvent event) { switch (mState) { - case SipSessionState.IN_CALL: - case SipSessionState.READY_TO_CALL: + case SipSession.State.IN_CALL: + case SipSession.State.READY_TO_CALL: Log.d(TAG, "Transaction terminated; do nothing"); break; default: @@ -664,27 +669,27 @@ class SipSessionGroup implements SipListener { ? event.getServerTransaction() : event.getClientTransaction(); - if ((current != target) && (mState != SipSessionState.PINGING)) { + if ((current != target) && (mState != SipSession.State.PINGING)) { Log.d(TAG, "not the current transaction; current=" + current + ", timed out=" + target); return; } switch (mState) { - case SipSessionState.REGISTERING: - case SipSessionState.DEREGISTERING: + case SipSession.State.REGISTERING: + case SipSession.State.DEREGISTERING: reset(); mProxy.onRegistrationTimeout(this); break; - case SipSessionState.INCOMING_CALL: - case SipSessionState.INCOMING_CALL_ANSWERING: - case SipSessionState.OUTGOING_CALL: - case SipSessionState.OUTGOING_CALL_CANCELING: + case SipSession.State.INCOMING_CALL: + case SipSession.State.INCOMING_CALL_ANSWERING: + case SipSession.State.OUTGOING_CALL: + case SipSession.State.OUTGOING_CALL_CANCELING: onError(SipErrorCode.TIME_OUT, event.toString()); break; - case SipSessionState.PINGING: + case SipSession.State.PINGING: reset(); mReRegisterFlag = true; - mState = SipSessionState.READY_TO_CALL; + mState = SipSession.State.READY_TO_CALL; break; default: @@ -758,7 +763,7 @@ class SipSessionGroup implements SipListener { switch (statusCode) { case Response.OK: int state = mState; - onRegistrationDone((state == SipSessionState.REGISTERING) + onRegistrationDone((state == SipSession.State.REGISTERING) ? getExpiryTime(((ResponseEvent) evt).getResponse()) : -1); mLastNonce = null; @@ -845,7 +850,7 @@ class SipSessionGroup implements SipListener { generateTag()); mDialog = mClientTransaction.getDialog(); addSipSession(this); - mState = SipSessionState.OUTGOING_CALL; + mState = SipSession.State.OUTGOING_CALL; mProxy.onCalling(this); startSessionTimer(cmd.getTimeout()); return true; @@ -855,7 +860,7 @@ class SipSessionGroup implements SipListener { generateTag(), duration); mDialog = mClientTransaction.getDialog(); addSipSession(this); - mState = SipSessionState.REGISTERING; + mState = SipSession.State.REGISTERING; mProxy.onRegistering(this); return true; } else if (DEREGISTER == evt) { @@ -863,7 +868,7 @@ class SipSessionGroup implements SipListener { generateTag(), 0); mDialog = mClientTransaction.getDialog(); addSipSession(this); - mState = SipSessionState.DEREGISTERING; + mState = SipSession.State.DEREGISTERING; mProxy.onRegistering(this); return true; } @@ -878,7 +883,7 @@ class SipSessionGroup implements SipListener { mLocalProfile, ((MakeCallCommand) evt).getSessionDescription(), mServerTransaction); - mState = SipSessionState.INCOMING_CALL_ANSWERING; + mState = SipSession.State.INCOMING_CALL_ANSWERING; startSessionTimer(((MakeCallCommand) evt).getTimeout()); return true; } else if (END_CALL == evt) { @@ -919,8 +924,8 @@ class SipSessionGroup implements SipListener { int statusCode = response.getStatusCode(); switch (statusCode) { case Response.RINGING: - if (mState == SipSessionState.OUTGOING_CALL) { - mState = SipSessionState.OUTGOING_CALL_RING_BACK; + if (mState == SipSession.State.OUTGOING_CALL) { + mState = SipSession.State.OUTGOING_CALL_RING_BACK; mProxy.onRingingBack(this); cancelSessionTimer(); } @@ -963,7 +968,7 @@ class SipSessionGroup implements SipListener { // response comes back yet. We are cheating for not checking // response. mSipHelper.sendCancel(mClientTransaction); - mState = SipSessionState.OUTGOING_CALL_CANCELING; + mState = SipSession.State.OUTGOING_CALL_CANCELING; startSessionTimer(CANCEL_CALL_TIMER); return true; } @@ -1019,7 +1024,7 @@ class SipSessionGroup implements SipListener { } else if (isRequestEvent(Request.INVITE, evt)) { // got Re-INVITE RequestEvent event = mInviteReceived = (RequestEvent) evt; - mState = SipSessionState.INCOMING_CALL; + mState = SipSession.State.INCOMING_CALL; mPeerSessionDescription = extractContent(event.getRequest()); mServerTransaction = null; mProxy.onRinging(this, mPeerProfile, mPeerSessionDescription); @@ -1032,7 +1037,7 @@ class SipSessionGroup implements SipListener { // to change call mClientTransaction = mSipHelper.sendReinvite(mDialog, ((MakeCallCommand) evt).getSessionDescription()); - mState = SipSessionState.OUTGOING_CALL; + mState = SipSession.State.OUTGOING_CALL; startSessionTimer(((MakeCallCommand) evt).getTimeout()); return true; } @@ -1060,14 +1065,14 @@ class SipSessionGroup implements SipListener { } private void establishCall() { - mState = SipSessionState.IN_CALL; + mState = SipSession.State.IN_CALL; mInCall = true; cancelSessionTimer(); mProxy.onCallEstablished(this, mPeerSessionDescription); } private void fallbackToPreviousInCall(int errorCode, String message) { - mState = SipSessionState.IN_CALL; + mState = SipSession.State.IN_CALL; mProxy.onCallChangeFailed(this, errorCode, message); } @@ -1089,8 +1094,8 @@ class SipSessionGroup implements SipListener { private void onError(int errorCode, String message) { cancelSessionTimer(); switch (mState) { - case SipSessionState.REGISTERING: - case SipSessionState.DEREGISTERING: + case SipSession.State.REGISTERING: + case SipSession.State.DEREGISTERING: onRegistrationFailed(errorCode, message); break; default: @@ -1264,7 +1269,7 @@ class SipSessionGroup implements SipListener { private static boolean isLoggable(SipSessionImpl s) { if (s != null) { switch (s.mState) { - case SipSessionState.PINGING: + case SipSession.State.PINGING: return DEBUG_PING; } } diff --git a/services/java/com/android/server/sip/SipSessionListenerProxy.java b/services/java/com/android/server/sip/SipSessionListenerProxy.java index a4cd102..f8be0a8 100644 --- a/services/java/com/android/server/sip/SipSessionListenerProxy.java +++ b/services/java/com/android/server/sip/SipSessionListenerProxy.java @@ -40,7 +40,7 @@ class SipSessionListenerProxy extends ISipSessionListener.Stub { // One thread for each calling back. // Note: Guarantee ordering if the issue becomes important. Currently, // the chance of handling two callback events at a time is none. - new Thread(runnable).start(); + new Thread(runnable, "SipSessionCallbackThread").start(); } public void onCalling(final ISipSession session) { diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.cpp b/services/surfaceflinger/DisplayHardware/HWComposer.cpp index 129be4e..ff887e4 100644 --- a/services/surfaceflinger/DisplayHardware/HWComposer.cpp +++ b/services/surfaceflinger/DisplayHardware/HWComposer.cpp @@ -21,6 +21,7 @@ #include <sys/types.h> #include <utils/Errors.h> +#include <utils/String8.h> #include <hardware/hardware.h> @@ -100,5 +101,25 @@ hwc_layer_t* HWComposer::getLayers() const { return mList ? mList->hwLayers : 0; } +void HWComposer::dump(String8& result, char* buffer, size_t SIZE) const { + if (mHwc && mList) { + result.append("Hardware Composer state:\n"); + + snprintf(buffer, SIZE, " numHwLayers=%u, flags=%08x\n", + mList->numHwLayers, mList->flags); + result.append(buffer); + + for (size_t i=0 ; i<mList->numHwLayers ; i++) { + const hwc_layer_t& l(mList->hwLayers[i]); + snprintf(buffer, SIZE, " %8s | %08x | %08x | %02x | %04x | [%5d,%5d,%5d,%5d] | [%5d,%5d,%5d,%5d]\n", + l.compositionType ? "OVERLAY" : "FB", + l.hints, l.flags, l.transform, l.blending, + l.sourceCrop.left, l.sourceCrop.top, l.sourceCrop.right, l.sourceCrop.bottom, + l.displayFrame.left, l.displayFrame.top, l.displayFrame.right, l.displayFrame.bottom); + result.append(buffer); + } + } +} + // --------------------------------------------------------------------------- }; // namespace android diff --git a/services/surfaceflinger/DisplayHardware/HWComposer.h b/services/surfaceflinger/DisplayHardware/HWComposer.h index 22ff10c..5a9e9eb 100644 --- a/services/surfaceflinger/DisplayHardware/HWComposer.h +++ b/services/surfaceflinger/DisplayHardware/HWComposer.h @@ -27,6 +27,8 @@ namespace android { // --------------------------------------------------------------------------- +class String8; + class HWComposer { public: @@ -54,6 +56,9 @@ public: size_t getNumLayers() const; hwc_layer_t* getLayers() const; + // for debugging + void dump(String8& out, char* scratch, size_t SIZE) const; + private: hw_module_t const* mModule; hwc_composer_device_t* mHwc; diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index 06c86dd..b353bff 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -1533,6 +1533,7 @@ status_t SurfaceFlinger::dump(int fd, const Vector<String16>& args) hwc.initCheck()==NO_ERROR ? "present" : "not present", mDebugDisableHWC ? "disabled" : "enabled"); result.append(buffer); + hwc.dump(result, buffer, SIZE); const GraphicBufferAllocator& alloc(GraphicBufferAllocator::get()); alloc.dump(result); diff --git a/telephony/java/com/android/internal/telephony/sip/SipPhone.java b/telephony/java/com/android/internal/telephony/sip/SipPhone.java index 9fcf12d..08194d4 100755 --- a/telephony/java/com/android/internal/telephony/sip/SipPhone.java +++ b/telephony/java/com/android/internal/telephony/sip/SipPhone.java @@ -27,7 +27,7 @@ import android.net.sip.SipErrorCode; import android.net.sip.SipException; import android.net.sip.SipManager; import android.net.sip.SipProfile; -import android.net.sip.SipSessionState; +import android.net.sip.SipSession; import android.os.AsyncResult; import android.os.Handler; import android.os.Looper; @@ -73,7 +73,9 @@ import java.util.List; public class SipPhone extends SipPhoneBase { private static final String LOG_TAG = "SipPhone"; private static final boolean LOCAL_DEBUG = true; - private static final int SESSION_TIMEOUT = 8; // in seconds + private static final int TIMEOUT_MAKE_CALL = 15; // in seconds + private static final int TIMEOUT_ANSWER_CALL = 8; // in seconds + private static final int TIMEOUT_HOLD_CALL = 15; // in seconds // A call that is ringing or (call) waiting private SipCall ringingCall = new SipCall(); @@ -91,7 +93,7 @@ public class SipPhone extends SipPhoneBase { foregroundCall = new SipCall(); backgroundCall = new SipCall(); mProfile = profile; - mSipManager = SipManager.getInstance(context); + mSipManager = SipManager.newInstance(context); // FIXME: what's this for SIP? //Change the system property @@ -693,7 +695,7 @@ public class SipPhone extends SipPhoneBase { void acceptCall() throws CallStateException { try { - mSipAudioCall.answerCall(SESSION_TIMEOUT); + mSipAudioCall.answerCall(TIMEOUT_ANSWER_CALL); } catch (SipException e) { throw new CallStateException("acceptCall(): " + e); } @@ -710,8 +712,8 @@ public class SipPhone extends SipPhoneBase { void dial() throws SipException { setState(Call.State.DIALING); - mSipAudioCall = mSipManager.makeAudioCall(mContext, mProfile, - mPeer, null, SESSION_TIMEOUT); + mSipAudioCall = mSipManager.makeAudioCall(mProfile, mPeer, null, + TIMEOUT_MAKE_CALL); mSipAudioCall.setRingbackToneEnabled(false); mSipAudioCall.setListener(mAdapter); } @@ -719,7 +721,7 @@ public class SipPhone extends SipPhoneBase { void hold() throws CallStateException { setState(Call.State.HOLDING); try { - mSipAudioCall.holdCall(SESSION_TIMEOUT); + mSipAudioCall.holdCall(TIMEOUT_HOLD_CALL); } catch (SipException e) { throw new CallStateException("hold(): " + e); } @@ -729,7 +731,7 @@ public class SipPhone extends SipPhoneBase { mSipAudioCall.setAudioGroup(audioGroup); setState(Call.State.ACTIVE); try { - mSipAudioCall.continueCall(SESSION_TIMEOUT); + mSipAudioCall.continueCall(TIMEOUT_HOLD_CALL); } catch (SipException e) { throw new CallStateException("unhold(): " + e); } @@ -807,20 +809,20 @@ public class SipPhone extends SipPhoneBase { if (sipAudioCall.isOnHold()) return Call.State.HOLDING; int sessionState = sipAudioCall.getState(); switch (sessionState) { - case SipSessionState.READY_TO_CALL: return Call.State.IDLE; - case SipSessionState.INCOMING_CALL: - case SipSessionState.INCOMING_CALL_ANSWERING: return Call.State.INCOMING; - case SipSessionState.OUTGOING_CALL: return Call.State.DIALING; - case SipSessionState.OUTGOING_CALL_RING_BACK: return Call.State.ALERTING; - case SipSessionState.OUTGOING_CALL_CANCELING: return Call.State.DISCONNECTING; - case SipSessionState.IN_CALL: return Call.State.ACTIVE; + case SipSession.State.READY_TO_CALL: return Call.State.IDLE; + case SipSession.State.INCOMING_CALL: + case SipSession.State.INCOMING_CALL_ANSWERING: return Call.State.INCOMING; + case SipSession.State.OUTGOING_CALL: return Call.State.DIALING; + case SipSession.State.OUTGOING_CALL_RING_BACK: return Call.State.ALERTING; + case SipSession.State.OUTGOING_CALL_CANCELING: return Call.State.DISCONNECTING; + case SipSession.State.IN_CALL: return Call.State.ACTIVE; default: Log.w(LOG_TAG, "illegal connection state: " + sessionState); return Call.State.DISCONNECTED; } } - private abstract class SipAudioCallAdapter extends SipAudioCall.Adapter { + private abstract class SipAudioCallAdapter extends SipAudioCall.Listener { protected abstract void onCallEnded(Connection.DisconnectCause cause); protected abstract void onError(Connection.DisconnectCause cause); diff --git a/tests/BatteryWaster/src/com/android/batterywaster/BatteryWaster.java b/tests/BatteryWaster/src/com/android/batterywaster/BatteryWaster.java index 48c4520..5fb09a7 100644 --- a/tests/BatteryWaster/src/com/android/batterywaster/BatteryWaster.java +++ b/tests/BatteryWaster/src/com/android/batterywaster/BatteryWaster.java @@ -25,6 +25,7 @@ import android.os.BatteryManager; import android.os.Bundle; import android.os.PowerManager; import android.view.View; +import android.view.WindowManager; import android.widget.CheckBox; import android.widget.TextView; @@ -38,7 +39,6 @@ public class BatteryWaster extends Activity { TextView mLog; DateFormat mDateFormat; IntentFilter mFilter; - PowerManager.WakeLock mWakeLock; PowerManager.WakeLock mPartialWakeLock; SpinThread mThread; @@ -65,24 +65,26 @@ public class BatteryWaster extends Activity { mFilter.addAction(Intent.ACTION_POWER_CONNECTED); PowerManager pm = (PowerManager)getSystemService(POWER_SERVICE); - mWakeLock = pm.newWakeLock(PowerManager.FULL_WAKE_LOCK, "BatteryWaster"); - mWakeLock.setReferenceCounted(false); mPartialWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "BatteryWaster"); mPartialWakeLock.setReferenceCounted(false); } @Override - public void onPause() { - super.onPause(); - stopRunning(); + public void onResume() { + super.onResume(); + if (((CheckBox)findViewById(R.id.checkbox)).isChecked()) { + startRunning(); + } + if (((CheckBox)findViewById(R.id.checkbox_wake)).isChecked()) { + mWaking = true; + updateWakeLock(); + } } @Override public void onDestroy() { super.onDestroy(); - if (mWakeLock.isHeld()) { - mWakeLock.release(); - } + stopRunning(); if (mPartialWakeLock.isHeld()) { mPartialWakeLock.release(); } @@ -140,13 +142,9 @@ public class BatteryWaster extends Activity { void updateWakeLock() { if (mWasting) { - if (!mWakeLock.isHeld()) { - mWakeLock.acquire(); - } + getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); } else { - if (mWakeLock.isHeld()) { - mWakeLock.release(); - } + getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); } if (mWaking) { if (!mPartialWakeLock.isHeld()) { diff --git a/tools/aapt/Bundle.h b/tools/aapt/Bundle.h index a1bc241..c5aa573 100644 --- a/tools/aapt/Bundle.h +++ b/tools/aapt/Bundle.h @@ -45,7 +45,7 @@ public: mRClassDir(NULL), mResourceIntermediatesDir(NULL), mManifestMinSdkVersion(NULL), mMinSdkVersion(NULL), mTargetSdkVersion(NULL), mMaxSdkVersion(NULL), mVersionCode(NULL), mVersionName(NULL), mCustomPackage(NULL), - mMaxResVersion(NULL), mDebugMode(false), + mMaxResVersion(NULL), mDebugMode(false), mProduct(NULL), mArgc(0), mArgv(NULL) {} ~Bundle(void) {} @@ -139,6 +139,8 @@ public: void setMaxResVersion(const char * val) { mMaxResVersion = val; } bool getDebugMode() { return mDebugMode; } void setDebugMode(bool val) { mDebugMode = val; } + const char* getProduct() const { return mProduct; } + void setProduct(const char * val) { mProduct = val; } /* * Set and get the file specification. @@ -237,6 +239,7 @@ private: const char* mCustomPackage; const char* mMaxResVersion; bool mDebugMode; + const char* mProduct; /* file specification */ int mArgc; diff --git a/tools/aapt/Main.cpp b/tools/aapt/Main.cpp index 71c023d..739b01f 100644 --- a/tools/aapt/Main.cpp +++ b/tools/aapt/Main.cpp @@ -67,6 +67,7 @@ void usage(void) " [-A asset-source-dir] [-G class-list-file] [-P public-definitions-file] \\\n" " [-S resource-sources [-S resource-sources ...]] " " [-F apk-file] [-J R-file-dir] \\\n" + " [--product product1,product2,...] \\\n" " [raw-files-dir [raw-files-dir] ...]\n" "\n" " Package the android resources. It will read assets and resources that are\n" @@ -154,6 +155,9 @@ void usage(void) " components target the given package. Useful when used in\n" " conjunction with --rename-manifest-package to fix tests against\n" " a package that has been renamed.\n" + " --product\n" + " Specifies which variant to choose for strings that have\n" + " product variants\n" " --utf16\n" " changes default encoding for resources to UTF-16. Only useful when API\n" " level is set to 7 or higher where the default encoding is UTF-8.\n"); @@ -484,6 +488,15 @@ int main(int argc, char* const argv[]) bundle.setInstrumentationPackageNameOverride(argv[0]); } else if (strcmp(cp, "-auto-add-overlay") == 0) { bundle.setAutoAddOverlay(true); + } else if (strcmp(cp, "-product") == 0) { + argc--; + argv++; + if (!argc) { + fprintf(stderr, "ERROR: No argument supplied for '--product' option\n"); + wantUsage = true; + goto bail; + } + bundle.setProduct(argv[0]); } else { fprintf(stderr, "ERROR: Unknown option '-%s'\n", cp); wantUsage = true; diff --git a/tools/aapt/ResourceTable.cpp b/tools/aapt/ResourceTable.cpp index f40a877..90a6256 100644 --- a/tools/aapt/ResourceTable.cpp +++ b/tools/aapt/ResourceTable.cpp @@ -574,6 +574,7 @@ status_t parseAndAddBag(Bundle* bundle, const String16& itemIdent, int32_t curFormat, bool isFormatted, + const String16& product, bool pseudolocalize, const bool overwrite, ResourceTable* outTable) @@ -606,6 +607,32 @@ status_t parseAndAddBag(Bundle* bundle, return err; } +/* + * Returns true if needle is one of the elements in the comma-separated list + * haystack, false otherwise. + */ +bool isInProductList(const String16& needle, const String16& haystack) { + const char16_t *needle2 = needle.string(); + const char16_t *haystack2 = haystack.string(); + size_t needlesize = needle.size(); + + while (*haystack2 != '\0') { + if (strncmp16(haystack2, needle2, needlesize) == 0) { + if (haystack2[needlesize] == '\0' || haystack2[needlesize] == ',') { + return true; + } + } + + while (*haystack2 != '\0' && *haystack2 != ',') { + haystack2++; + } + if (*haystack2 == ',') { + haystack2++; + } + } + + return false; +} status_t parseAndAddEntry(Bundle* bundle, const sp<AaptFile>& in, @@ -618,6 +645,7 @@ status_t parseAndAddEntry(Bundle* bundle, bool curIsStyled, int32_t curFormat, bool isFormatted, + const String16& product, bool pseudolocalize, const bool overwrite, ResourceTable* outTable) @@ -634,6 +662,47 @@ status_t parseAndAddEntry(Bundle* bundle, return err; } + /* + * If a product type was specified on the command line + * and also in the string, and the two are not the same, + * return without adding the string. + */ + + const char *bundleProduct = bundle->getProduct(); + if (bundleProduct == NULL) { + bundleProduct = ""; + } + + if (product.size() != 0) { + /* + * If the command-line-specified product is empty, only "default" + * matches. Other variants are skipped. This is so generation + * of the R.java file when the product is not known is predictable. + */ + + if (bundleProduct[0] == '\0') { + if (strcmp16(String16("default").string(), product.string()) != 0) { + return NO_ERROR; + } + } else { + /* + * The command-line product is not empty. + * If the product for this string is on the command-line list, + * it matches. "default" also matches, but only if nothing + * else has matched already. + */ + + if (isInProductList(product, String16(bundleProduct))) { + ; + } else if (strcmp16(String16("default").string(), product.string()) == 0 && + !outTable->hasBagOrEntry(myPackage, curType, ident)) { + ; + } else { + return NO_ERROR; + } + } + } + NOISY(printf("Adding resource entry l=%c%c c=%c%c orien=%d d=%d id=%s: %s\n", config.language[0], config.language[1], config.country[0], config.country[1], @@ -713,6 +782,7 @@ status_t compileResourceFile(Bundle* bundle, const String16 translatable16("translatable"); const String16 formatted16("formatted"); const String16 false16("false"); + const String16 product16("product"); const String16 myPackage(assets->getPackage()); @@ -760,6 +830,7 @@ status_t compileResourceFile(Bundle* bundle, bool curIsStyled = false; bool curIsPseudolocalizable = false; bool curIsFormatted = fileIsTranslatable; + String16 curProduct; bool localHasErrors = false; if (strcmp16(block.getElementName(&len), skip16.string()) == 0) { @@ -1157,6 +1228,8 @@ status_t compileResourceFile(Bundle* bundle, translatable.setTo(block.getAttributeStringValue(i, &length)); } else if (strcmp16(attr, formatted16.string()) == 0) { formatted.setTo(block.getAttributeStringValue(i, &length)); + } else if (strcmp16(attr, product16.string()) == 0) { + curProduct.setTo(block.getAttributeStringValue(i, &length)); } } @@ -1374,7 +1447,7 @@ status_t compileResourceFile(Bundle* bundle, err = parseAndAddBag(bundle, in, &block, curParams, myPackage, curType, ident, parentIdent, itemIdent, curFormat, curIsFormatted, - false, overwrite, outTable); + curProduct, false, overwrite, outTable); if (err == NO_ERROR) { if (curIsPseudolocalizable && localeIsDefined(curParams) && bundle->getPseudolocalize()) { @@ -1383,7 +1456,7 @@ status_t compileResourceFile(Bundle* bundle, block.setPosition(parserPosition); err = parseAndAddBag(bundle, in, &block, pseudoParams, myPackage, curType, ident, parentIdent, itemIdent, curFormat, - curIsFormatted, true, overwrite, outTable); + curIsFormatted, curProduct, true, overwrite, outTable); #endif } } @@ -1407,7 +1480,7 @@ status_t compileResourceFile(Bundle* bundle, err = parseAndAddEntry(bundle, in, &block, curParams, myPackage, curType, ident, *curTag, curIsStyled, curFormat, curIsFormatted, - false, overwrite, outTable); + curProduct, false, overwrite, outTable); if (err < NO_ERROR) { // Why err < NO_ERROR instead of err != NO_ERROR? hasErrors = localHasErrors = true; @@ -1419,7 +1492,8 @@ status_t compileResourceFile(Bundle* bundle, block.setPosition(parserPosition); err = parseAndAddEntry(bundle, in, &block, pseudoParams, myPackage, curType, ident, *curTag, curIsStyled, curFormat, - curIsFormatted, true, overwrite, outTable); + curIsFormatted, curProduct, + true, overwrite, outTable); if (err != NO_ERROR) { hasErrors = localHasErrors = true; } diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/annotations/Nullable.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/annotations/Nullable.java new file mode 100755 index 0000000..0689c92 --- /dev/null +++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/annotations/Nullable.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.tools.layoutlib.annotations; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Denotes a parameter or field can be null. + * <p/> + * When decorating a method call parameter, this denotes the parameter can + * legitimately be null and the method will gracefully deal with it. Typically used + * on optional parameters. + * <p/> + * When decorating a method, this denotes the method might legitimately return null. + * <p/> + * This is a marker annotation and it has no specific attributes. + */ +@Retention(RetentionPolicy.SOURCE) +public @interface Nullable { +} diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/annotations/VisibleForTesting.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/annotations/VisibleForTesting.java new file mode 100755 index 0000000..e4e016b --- /dev/null +++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/annotations/VisibleForTesting.java @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.tools.layoutlib.annotations; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +/** + * Denotes that the class, method or field has its visibility relaxed so + * that unit tests can access it. + * <p/> + * The <code>visibility</code> argument can be used to specific what the original + * visibility should have been if it had not been made public or package-private for testing. + * The default is to consider the element private. + */ +@Retention(RetentionPolicy.SOURCE) +public @interface VisibleForTesting { + /** + * Intended visibility if the element had not been made public or package-private for + * testing. + */ + enum Visibility { + /** The element should be considered protected. */ + PROTECTED, + /** The element should be considered package-private. */ + PACKAGE, + /** The element should be considered private. */ + PRIVATE + } + + /** + * Intended visibility if the element had not been made public or package-private for testing. + * If not specified, one should assume the element originally intended to be private. + */ + Visibility visibility() default Visibility.PRIVATE; +} diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/ClassHasNativeVisitor.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/ClassHasNativeVisitor.java index 5424efa..722dce2 100644 --- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/ClassHasNativeVisitor.java +++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/ClassHasNativeVisitor.java @@ -16,6 +16,9 @@ package com.android.tools.layoutlib.create; +import com.android.tools.layoutlib.annotations.VisibleForTesting; +import com.android.tools.layoutlib.annotations.VisibleForTesting.Visibility; + import org.objectweb.asm.AnnotationVisitor; import org.objectweb.asm.Attribute; import org.objectweb.asm.ClassVisitor; @@ -27,13 +30,18 @@ import org.objectweb.asm.Opcodes; * Indicates if a class contains any native methods. */ public class ClassHasNativeVisitor implements ClassVisitor { - + private boolean mHasNativeMethods = false; - + public boolean hasNativeMethods() { return mHasNativeMethods; } + @VisibleForTesting(visibility=Visibility.PRIVATE) + protected void setHasNativeMethods(boolean hasNativeMethods, String methodName) { + mHasNativeMethods = hasNativeMethods; + } + public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { // pass @@ -65,7 +73,9 @@ public class ClassHasNativeVisitor implements ClassVisitor { public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { - mHasNativeMethods |= ((access & Opcodes.ACC_NATIVE) != 0); + if ((access & Opcodes.ACC_NATIVE) != 0) { + setHasNativeMethods(true, name); + } return null; } diff --git a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/ClassHasNativeVisitorTest.java b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/ClassHasNativeVisitorTest.java new file mode 100644 index 0000000..d6916ae --- /dev/null +++ b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/ClassHasNativeVisitorTest.java @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.tools.layoutlib.create; + +import static org.junit.Assert.*; + +import org.junit.Test; +import org.objectweb.asm.ClassReader; + +import java.io.IOException; +import java.util.ArrayList; + + +/** + * Tests {@link ClassHasNativeVisitor}. + */ +public class ClassHasNativeVisitorTest { + + @Test + public void testHasNative() throws IOException { + MockClassHasNativeVisitor cv = new MockClassHasNativeVisitor(); + ClassReader cr = new ClassReader( + "com.android.tools.layoutlib.create.ClassHasNativeVisitorTest$ClassWithNative"); + + cr.accept(cv, 0 /* flags */); + assertArrayEquals(new String[] { "native_method" }, cv.getMethodsFound()); + assertTrue(cv.hasNativeMethods()); + } + + @Test + public void testHasNoNative() throws IOException { + MockClassHasNativeVisitor cv = new MockClassHasNativeVisitor(); + ClassReader cr = new ClassReader( + "com.android.tools.layoutlib.create.ClassHasNativeVisitorTest$ClassWithoutNative"); + + cr.accept(cv, 0 /* flags */); + assertArrayEquals(new String[0], cv.getMethodsFound()); + assertFalse(cv.hasNativeMethods()); + } + + /** + * Overrides {@link ClassHasNativeVisitor} to collec the name of the native methods found. + */ + private static class MockClassHasNativeVisitor extends ClassHasNativeVisitor { + private ArrayList<String> mMethodsFound = new ArrayList<String>(); + + public String[] getMethodsFound() { + return mMethodsFound.toArray(new String[mMethodsFound.size()]); + } + + @Override + protected void setHasNativeMethods(boolean hasNativeMethods, String methodName) { + if (hasNativeMethods) { + mMethodsFound.add(methodName); + } + super.setHasNativeMethods(hasNativeMethods, methodName); + } + } + + /** + * Dummy test class with a native method. + */ + public static class ClassWithNative { + public ClassWithNative() { + } + + public void callTheNativeMethod() { + native_method(); + } + + private native void native_method(); + } + + /** + * Dummy test class with no native method. + */ + public static class ClassWithoutNative { + public ClassWithoutNative() { + } + + public void someMethod() { + } + } +} diff --git a/voip/java/android/net/sip/SdpSessionDescription.java b/voip/java/android/net/sip/SdpSessionDescription.java deleted file mode 100644 index f6ae837..0000000 --- a/voip/java/android/net/sip/SdpSessionDescription.java +++ /dev/null @@ -1,428 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net.sip; - -import gov.nist.javax.sdp.SessionDescriptionImpl; -import gov.nist.javax.sdp.fields.AttributeField; -import gov.nist.javax.sdp.fields.ConnectionField; -import gov.nist.javax.sdp.fields.MediaField; -import gov.nist.javax.sdp.fields.OriginField; -import gov.nist.javax.sdp.fields.ProtoVersionField; -import gov.nist.javax.sdp.fields.SessionNameField; -import gov.nist.javax.sdp.fields.TimeField; -import gov.nist.javax.sdp.parser.SDPAnnounceParser; - -import android.util.Log; - -import java.text.ParseException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Vector; -import javax.sdp.Connection; -import javax.sdp.MediaDescription; -import javax.sdp.SdpException; - -/** - * A session description that follows SDP (Session Description Protocol). - * Refer to <a href="http://tools.ietf.org/html/rfc4566">RFC 4566</a>. - * @hide - */ -public class SdpSessionDescription extends SessionDescription { - private static final String TAG = "SDP"; - private static final String AUDIO = "audio"; - private static final String RTPMAP = "rtpmap"; - private static final String PTIME = "ptime"; - private static final String SENDONLY = "sendonly"; - private static final String RECVONLY = "recvonly"; - private static final String INACTIVE = "inactive"; - - private SessionDescriptionImpl mSessionDescription; - - /** - * The audio codec information parsed from "rtpmap". - */ - public static class AudioCodec { - public final int payloadType; - public final String name; - public final int sampleRate; - public final int sampleCount; - - public AudioCodec(int payloadType, String name, int sampleRate, - int sampleCount) { - this.payloadType = payloadType; - this.name = name; - this.sampleRate = sampleRate; - this.sampleCount = sampleCount; - } - } - - /** - * The builder class used to create an {@link SdpSessionDescription} object. - */ - public static class Builder { - private SdpSessionDescription mSdp = new SdpSessionDescription(); - private SessionDescriptionImpl mSessionDescription; - - public Builder(String sessionName) throws SdpException { - mSessionDescription = new SessionDescriptionImpl(); - mSdp.mSessionDescription = mSessionDescription; - try { - ProtoVersionField proto = new ProtoVersionField(); - proto.setVersion(0); - mSessionDescription.addField(proto); - - TimeField time = new TimeField(); - time.setZero(); - mSessionDescription.addField(time); - - SessionNameField session = new SessionNameField(); - session.setValue(sessionName); - mSessionDescription.addField(session); - } catch (Exception e) { - throwSdpException(e); - } - } - - public Builder setConnectionInfo(String networkType, String addressType, - String addr) throws SdpException { - try { - ConnectionField connection = new ConnectionField(); - connection.setNetworkType(networkType); - connection.setAddressType(addressType); - connection.setAddress(addr); - mSessionDescription.addField(connection); - } catch (Exception e) { - throwSdpException(e); - } - return this; - } - - public Builder setOrigin(SipProfile user, long sessionId, - long sessionVersion, String networkType, String addressType, - String address) throws SdpException { - try { - OriginField origin = new OriginField(); - origin.setUsername(user.getUserName()); - origin.setSessionId(sessionId); - origin.setSessionVersion(sessionVersion); - origin.setAddressType(addressType); - origin.setNetworkType(networkType); - origin.setAddress(address); - mSessionDescription.addField(origin); - } catch (Exception e) { - throwSdpException(e); - } - return this; - } - - public Builder addMedia(String media, int port, int numPorts, - String transport, Integer... types) throws SdpException { - MediaField field = new MediaField(); - Vector<Integer> typeVector = new Vector<Integer>(); - Collections.addAll(typeVector, types); - try { - field.setMediaType(media); - field.setMediaPort(port); - field.setPortCount(numPorts); - field.setProtocol(transport); - field.setMediaFormats(typeVector); - mSessionDescription.addField(field); - } catch (Exception e) { - throwSdpException(e); - } - return this; - } - - public Builder addMediaAttribute(String type, String name, String value) - throws SdpException { - try { - MediaDescription md = mSdp.getMediaDescription(type); - if (md == null) { - throw new SdpException("Should add media first!"); - } - AttributeField attribute = new AttributeField(); - attribute.setName(name); - attribute.setValueAllowNull(value); - mSessionDescription.addField(attribute); - } catch (Exception e) { - throwSdpException(e); - } - return this; - } - - public Builder addSessionAttribute(String name, String value) - throws SdpException { - try { - AttributeField attribute = new AttributeField(); - attribute.setName(name); - attribute.setValueAllowNull(value); - mSessionDescription.addField(attribute); - } catch (Exception e) { - throwSdpException(e); - } - return this; - } - - private void throwSdpException(Exception e) throws SdpException { - if (e instanceof SdpException) { - throw (SdpException) e; - } else { - throw new SdpException(e.toString(), e); - } - } - - public String build() { - return mSdp.toString(); - } - } - - private SdpSessionDescription() { - } - - /** - * Constructor. - * - * @param sdpString an SDP session description to parse - */ - public SdpSessionDescription(String sdpString) throws SdpException { - try { - mSessionDescription = new SDPAnnounceParser(sdpString).parse(); - } catch (ParseException e) { - throw new SdpException(e.toString(), e); - } - verify(); - } - - /** - * Constructor. - * - * @param content a raw SDP session description to parse - */ - public SdpSessionDescription(byte[] content) throws SdpException { - this(new String(content)); - } - - private void verify() throws SdpException { - // make sure the syntax is correct over the fields we're interested in - Vector<MediaDescription> descriptions = (Vector<MediaDescription>) - mSessionDescription.getMediaDescriptions(false); - for (MediaDescription md : descriptions) { - md.getMedia().getMediaPort(); - Connection connection = md.getConnection(); - if (connection != null) connection.getAddress(); - md.getMedia().getFormats(); - } - Connection connection = mSessionDescription.getConnection(); - if (connection != null) connection.getAddress(); - } - - /** - * Gets the connection address of the media. - * - * @param type the media type; e.g., "AUDIO" - * @return the media connection address of the peer - */ - public String getPeerMediaAddress(String type) { - try { - MediaDescription md = getMediaDescription(type); - Connection connection = md.getConnection(); - if (connection == null) { - connection = mSessionDescription.getConnection(); - } - return ((connection == null) ? null : connection.getAddress()); - } catch (SdpException e) { - // should not occur - return null; - } - } - - /** - * Gets the connection port number of the media. - * - * @param type the media type; e.g., "AUDIO" - * @return the media connection port number of the peer - */ - public int getPeerMediaPort(String type) { - try { - MediaDescription md = getMediaDescription(type); - return md.getMedia().getMediaPort(); - } catch (SdpException e) { - // should not occur - return -1; - } - } - - private boolean containsAttribute(String type, String name) { - if (name == null) return false; - MediaDescription md = getMediaDescription(type); - Vector<AttributeField> v = (Vector<AttributeField>) - md.getAttributeFields(); - for (AttributeField field : v) { - if (name.equals(field.getAttribute().getName())) return true; - } - return false; - } - - /** - * Checks if the media is "sendonly". - * - * @param type the media type; e.g., "AUDIO" - * @return true if the media is "sendonly" - */ - public boolean isSendOnly(String type) { - boolean answer = containsAttribute(type, SENDONLY); - Log.d(TAG, " sendonly? " + answer); - return answer; - } - - /** - * Checks if the media is "recvonly". - * - * @param type the media type; e.g., "AUDIO" - * @return true if the media is "recvonly" - */ - public boolean isReceiveOnly(String type) { - boolean answer = containsAttribute(type, RECVONLY); - Log.d(TAG, " recvonly? " + answer); - return answer; - } - - /** - * Checks if the media is in sending; i.e., not "recvonly" and not - * "inactive". - * - * @param type the media type; e.g., "AUDIO" - * @return true if the media is sending - */ - public boolean isSending(String type) { - boolean answer = !containsAttribute(type, RECVONLY) - && !containsAttribute(type, INACTIVE); - - Log.d(TAG, " sending? " + answer); - return answer; - } - - /** - * Checks if the media is in receiving; i.e., not "sendonly" and not - * "inactive". - * - * @param type the media type; e.g., "AUDIO" - * @return true if the media is receiving - */ - public boolean isReceiving(String type) { - boolean answer = !containsAttribute(type, SENDONLY) - && !containsAttribute(type, INACTIVE); - Log.d(TAG, " receiving? " + answer); - return answer; - } - - private AudioCodec parseAudioCodec(String rtpmap, int ptime) { - String[] ss = rtpmap.split(" "); - int payloadType = Integer.parseInt(ss[0]); - - ss = ss[1].split("/"); - String name = ss[0]; - int sampleRate = Integer.parseInt(ss[1]); - int channelCount = 1; - if (ss.length > 2) channelCount = Integer.parseInt(ss[2]); - int sampleCount = sampleRate / (1000 / ptime) * channelCount; - return new AudioCodec(payloadType, name, sampleRate, sampleCount); - } - - /** - * Gets the list of audio codecs in this session description. - * - * @return the list of audio codecs in this session description - */ - public List<AudioCodec> getAudioCodecs() { - MediaDescription md = getMediaDescription(AUDIO); - if (md == null) return new ArrayList<AudioCodec>(); - - // FIXME: what happens if ptime is missing - int ptime = 20; - try { - String value = md.getAttribute(PTIME); - if (value != null) ptime = Integer.parseInt(value); - } catch (Throwable t) { - Log.w(TAG, "getCodecs(): ignored: " + t); - } - - List<AudioCodec> codecs = new ArrayList<AudioCodec>(); - Vector<AttributeField> v = (Vector<AttributeField>) - md.getAttributeFields(); - for (AttributeField field : v) { - try { - if (RTPMAP.equals(field.getName())) { - AudioCodec codec = parseAudioCodec(field.getValue(), ptime); - if (codec != null) codecs.add(codec); - } - } catch (Throwable t) { - Log.w(TAG, "getCodecs(): ignored: " + t); - } - } - return codecs; - } - - /** - * Gets the media description of the specified type. - * - * @param type the media type; e.g., "AUDIO" - * @return the media description of the specified type - */ - public MediaDescription getMediaDescription(String type) { - MediaDescription[] all = getMediaDescriptions(); - if ((all == null) || (all.length == 0)) return null; - for (MediaDescription md : all) { - String t = md.getMedia().getMedia(); - if (t.equalsIgnoreCase(type)) return md; - } - return null; - } - - /** - * Gets all the media descriptions in this session description. - * - * @return all the media descriptions in this session description - */ - public MediaDescription[] getMediaDescriptions() { - try { - Vector<MediaDescription> descriptions = (Vector<MediaDescription>) - mSessionDescription.getMediaDescriptions(false); - MediaDescription[] all = new MediaDescription[descriptions.size()]; - return descriptions.toArray(all); - } catch (SdpException e) { - Log.e(TAG, "getMediaDescriptions", e); - } - return null; - } - - @Override - public String getType() { - return "sdp"; - } - - @Override - public byte[] getContent() { - return mSessionDescription.toString().getBytes(); - } - - @Override - public String toString() { - return mSessionDescription.toString(); - } -} diff --git a/voip/java/android/net/sip/SessionDescription.aidl b/voip/java/android/net/sip/SessionDescription.aidl deleted file mode 100644 index a120d16..0000000 --- a/voip/java/android/net/sip/SessionDescription.aidl +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright (C) 2010, The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net.sip; - -parcelable SessionDescription; diff --git a/voip/java/android/net/sip/SessionDescription.java b/voip/java/android/net/sip/SessionDescription.java deleted file mode 100644 index d476f0b..0000000 --- a/voip/java/android/net/sip/SessionDescription.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net.sip; - -import android.os.Parcel; -import android.os.Parcelable; - -/** - * Abstract class of a session description. - * @hide - */ -public abstract class SessionDescription implements Parcelable { - /** @hide */ - public static final Parcelable.Creator<SessionDescription> CREATOR = - new Parcelable.Creator<SessionDescription>() { - public SessionDescription createFromParcel(Parcel in) { - return new SessionDescriptionImpl(in); - } - - public SessionDescription[] newArray(int size) { - return new SessionDescriptionImpl[size]; - } - }; - - /** - * Gets the type of the session description; e.g., "SDP". - * - * @return the session description type - */ - public abstract String getType(); - - /** - * Gets the raw content of the session description. - * - * @return the content of the session description - */ - public abstract byte[] getContent(); - - /** @hide */ - public void writeToParcel(Parcel out, int flags) { - out.writeString(getType()); - out.writeByteArray(getContent()); - } - - /** @hide */ - public int describeContents() { - return 0; - } - - private static class SessionDescriptionImpl extends SessionDescription { - private String mType; - private byte[] mContent; - - SessionDescriptionImpl(Parcel in) { - mType = in.readString(); - mContent = in.createByteArray(); - } - - @Override - public String getType() { - return mType; - } - - @Override - public byte[] getContent() { - return mContent; - } - } -} diff --git a/voip/java/android/net/sip/SipAudioCall.java b/voip/java/android/net/sip/SipAudioCall.java index 0069fe0..2135fcb 100644 --- a/voip/java/android/net/sip/SipAudioCall.java +++ b/voip/java/android/net/sip/SipAudioCall.java @@ -16,120 +16,184 @@ package android.net.sip; +import android.content.Context; +import android.media.AudioManager; +import android.media.Ringtone; +import android.media.RingtoneManager; +import android.media.ToneGenerator; +import android.net.Uri; +import android.net.rtp.AudioCodec; import android.net.rtp.AudioGroup; import android.net.rtp.AudioStream; +import android.net.rtp.RtpStream; +import android.net.sip.SimpleSessionDescription.Media; +import android.net.wifi.WifiManager; import android.os.Message; +import android.os.RemoteException; +import android.os.Vibrator; +import android.provider.Settings; +import android.util.Log; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; /** - * Interface for making audio calls over SIP. - * @hide + * Class that handles an audio call over SIP. */ -public interface SipAudioCall { +/** @hide */ +public class SipAudioCall extends SipSessionAdapter { + private static final String TAG = SipAudioCall.class.getSimpleName(); + private static final boolean RELEASE_SOCKET = true; + private static final boolean DONT_RELEASE_SOCKET = false; + private static final int SESSION_TIMEOUT = 5; // in seconds + /** Listener class for all event callbacks. */ - public interface Listener { + public static class Listener { /** * Called when the call object is ready to make another call. + * The default implementation calls {@link #onChange}. * * @param call the call object that is ready to make another call */ - void onReadyToCall(SipAudioCall call); + public void onReadyToCall(SipAudioCall call) { + onChanged(call); + } /** * Called when a request is sent out to initiate a new call. + * The default implementation calls {@link #onChange}. * * @param call the call object that carries out the audio call */ - void onCalling(SipAudioCall call); + public void onCalling(SipAudioCall call) { + onChanged(call); + } /** * Called when a new call comes in. + * The default implementation calls {@link #onChange}. * * @param call the call object that carries out the audio call * @param caller the SIP profile of the caller */ - void onRinging(SipAudioCall call, SipProfile caller); + public void onRinging(SipAudioCall call, SipProfile caller) { + onChanged(call); + } /** - * Called when a RINGING response is received for the INVITE request sent + * Called when a RINGING response is received for the INVITE request + * sent. The default implementation calls {@link #onChange}. * * @param call the call object that carries out the audio call */ - void onRingingBack(SipAudioCall call); + public void onRingingBack(SipAudioCall call) { + onChanged(call); + } /** * Called when the session is established. + * The default implementation calls {@link #onChange}. * * @param call the call object that carries out the audio call */ - void onCallEstablished(SipAudioCall call); + public void onCallEstablished(SipAudioCall call) { + onChanged(call); + } /** * Called when the session is terminated. + * The default implementation calls {@link #onChange}. * * @param call the call object that carries out the audio call */ - void onCallEnded(SipAudioCall call); + public void onCallEnded(SipAudioCall call) { + onChanged(call); + } /** * Called when the peer is busy during session initialization. + * The default implementation calls {@link #onChange}. * * @param call the call object that carries out the audio call */ - void onCallBusy(SipAudioCall call); + public void onCallBusy(SipAudioCall call) { + onChanged(call); + } /** * Called when the call is on hold. + * The default implementation calls {@link #onChange}. * * @param call the call object that carries out the audio call */ - void onCallHeld(SipAudioCall call); + public void onCallHeld(SipAudioCall call) { + onChanged(call); + } /** - * Called when an error occurs. + * Called when an error occurs. The default implementation is no op. * * @param call the call object that carries out the audio call * @param errorCode error code of this error * @param errorMessage error message * @see SipErrorCode */ - void onError(SipAudioCall call, int errorCode, String errorMessage); + public void onError(SipAudioCall call, int errorCode, + String errorMessage) { + // no-op + } + + /** + * Called when an event occurs and the corresponding callback is not + * overridden. The default implementation is no op. Error events are + * not re-directed to this callback and are handled in {@link #onError}. + */ + public void onChanged(SipAudioCall call) { + // no-op + } } + private Context mContext; + private SipProfile mLocalProfile; + private SipAudioCall.Listener mListener; + private SipSession mSipSession; + + private long mSessionId = System.currentTimeMillis(); + private String mPeerSd; + + private AudioStream mAudioStream; + private AudioGroup mAudioGroup; + + private boolean mInCall = false; + private boolean mMuted = false; + private boolean mHold = false; + + private boolean mRingbackToneEnabled = true; + private boolean mRingtoneEnabled = true; + private Ringtone mRingtone; + private ToneGenerator mRingbackTone; + + private SipProfile mPendingCallRequest; + private WifiManager mWm; + private WifiManager.WifiLock mWifiHighPerfLock; + + private int mErrorCode = SipErrorCode.NO_ERROR; + private String mErrorMessage; + /** - * The adapter class for {@link Listener}. The default implementation of - * all callback methods is no-op. + * Creates a call object with the local SIP profile. + * @param context the context for accessing system services such as + * ringtone, audio, WIFI etc */ - public class Adapter implements Listener { - protected void onChanged(SipAudioCall call) { - } - public void onReadyToCall(SipAudioCall call) { - onChanged(call); - } - public void onCalling(SipAudioCall call) { - onChanged(call); - } - public void onRinging(SipAudioCall call, SipProfile caller) { - onChanged(call); - } - public void onRingingBack(SipAudioCall call) { - onChanged(call); - } - public void onCallEstablished(SipAudioCall call) { - onChanged(call); - } - public void onCallEnded(SipAudioCall call) { - onChanged(call); - } - public void onCallBusy(SipAudioCall call) { - onChanged(call); - } - public void onCallHeld(SipAudioCall call) { - onChanged(call); - } - public void onError(SipAudioCall call, int errorCode, - String errorMessage) { - onChanged(call); - } + public SipAudioCall(Context context, SipProfile localProfile) { + mContext = context; + mLocalProfile = localProfile; + mWm = (WifiManager) context.getSystemService(Context.WIFI_SERVICE); } /** @@ -139,7 +203,9 @@ public interface SipAudioCall { * @param listener to listen to the audio call events of this object * @see #setListener(Listener, boolean) */ - void setListener(Listener listener); + public void setListener(SipAudioCall.Listener listener) { + setListener(listener, false); + } /** * Sets the listener to listen to the audio call events. A @@ -150,44 +216,355 @@ public interface SipAudioCall { * @param callbackImmediately set to true if the caller wants to be called * back immediately on the current state */ - void setListener(Listener listener, boolean callbackImmediately); + public void setListener(SipAudioCall.Listener listener, + boolean callbackImmediately) { + mListener = listener; + try { + if ((listener == null) || !callbackImmediately) { + // do nothing + } else if (mErrorCode != SipErrorCode.NO_ERROR) { + listener.onError(this, mErrorCode, mErrorMessage); + } else if (mInCall) { + if (mHold) { + listener.onCallHeld(this); + } else { + listener.onCallEstablished(this); + } + } else { + int state = getState(); + switch (state) { + case SipSession.State.READY_TO_CALL: + listener.onReadyToCall(this); + break; + case SipSession.State.INCOMING_CALL: + listener.onRinging(this, getPeerProfile()); + break; + case SipSession.State.OUTGOING_CALL: + listener.onCalling(this); + break; + case SipSession.State.OUTGOING_CALL_RING_BACK: + listener.onRingingBack(this); + break; + } + } + } catch (Throwable t) { + Log.e(TAG, "setListener()", t); + } + } + + /** + * Checks if the call is established. + * + * @return true if the call is established + */ + public synchronized boolean isInCall() { + return mInCall; + } + + /** + * Checks if the call is on hold. + * + * @return true if the call is on hold + */ + public synchronized boolean isOnHold() { + return mHold; + } /** * Closes this object. This object is not usable after being closed. */ - void close(); + public void close() { + close(true); + } + + private synchronized void close(boolean closeRtp) { + if (closeRtp) stopCall(RELEASE_SOCKET); + stopRingbackTone(); + stopRinging(); + + mInCall = false; + mHold = false; + mSessionId = System.currentTimeMillis(); + mErrorCode = SipErrorCode.NO_ERROR; + mErrorMessage = null; + + if (mSipSession != null) { + mSipSession.setListener(null); + mSipSession = null; + } + } /** - * Initiates an audio call to the specified profile. The attempt will be - * timed out if the call is not established within {@code timeout} seconds - * and {@code Listener.onError(SipAudioCall, SipErrorCode.TIME_OUT, String)} - * will be called. + * Gets the local SIP profile. * - * @param callee the SIP profile to make the call to - * @param sipManager the {@link SipManager} object to help make call with - * @param timeout the timeout value in seconds - * @see Listener.onError + * @return the local SIP profile */ - void makeCall(SipProfile callee, SipManager sipManager, int timeout) - throws SipException; + public synchronized SipProfile getLocalProfile() { + return mLocalProfile; + } /** - * Starts the audio for the established call. This method should be called - * after {@link Listener#onCallEstablished} is called. + * Gets the peer's SIP profile. + * + * @return the peer's SIP profile */ - void startAudio(); + public synchronized SipProfile getPeerProfile() { + return (mSipSession == null) ? null : mSipSession.getPeerProfile(); + } + + /** + * Gets the state of the {@link SipSession} that carries this call. + * The value returned must be one of the states in {@link SipSession.State}. + * + * @return the session state + */ + public synchronized int getState() { + if (mSipSession == null) return SipSession.State.READY_TO_CALL; + return mSipSession.getState(); + } + + + /** + * Gets the {@link SipSession} that carries this call. + * + * @return the session object that carries this call + * @hide + */ + public synchronized SipSession getSipSession() { + return mSipSession; + } + + private SipSession.Listener createListener() { + return new SipSession.Listener() { + @Override + public void onCalling(SipSession session) { + Log.d(TAG, "calling... " + session); + Listener listener = mListener; + if (listener != null) { + try { + listener.onCalling(SipAudioCall.this); + } catch (Throwable t) { + Log.i(TAG, "onCalling(): " + t); + } + } + } + + @Override + public void onRingingBack(SipSession session) { + Log.d(TAG, "sip call ringing back: " + session); + if (!mInCall) startRingbackTone(); + Listener listener = mListener; + if (listener != null) { + try { + listener.onRingingBack(SipAudioCall.this); + } catch (Throwable t) { + Log.i(TAG, "onRingingBack(): " + t); + } + } + } + + @Override + public synchronized void onRinging(SipSession session, + SipProfile peerProfile, String sessionDescription) { + if ((mSipSession == null) || !mInCall + || !session.getCallId().equals(mSipSession.getCallId())) { + // should not happen + session.endCall(); + return; + } + + // session changing request + try { + String answer = createAnswer(sessionDescription).encode(); + mSipSession.answerCall(answer, SESSION_TIMEOUT); + } catch (Throwable e) { + Log.e(TAG, "onRinging()", e); + session.endCall(); + } + } + + @Override + public void onCallEstablished(SipSession session, + String sessionDescription) { + stopRingbackTone(); + stopRinging(); + mPeerSd = sessionDescription; + Log.v(TAG, "onCallEstablished()" + mPeerSd); + + Listener listener = mListener; + if (listener != null) { + try { + if (mHold) { + listener.onCallHeld(SipAudioCall.this); + } else { + listener.onCallEstablished(SipAudioCall.this); + } + } catch (Throwable t) { + Log.i(TAG, "onCallEstablished(): " + t); + } + } + } + + @Override + public void onCallEnded(SipSession session) { + Log.d(TAG, "sip call ended: " + session); + Listener listener = mListener; + if (listener != null) { + try { + listener.onCallEnded(SipAudioCall.this); + } catch (Throwable t) { + Log.i(TAG, "onCallEnded(): " + t); + } + } + close(); + } + + @Override + public void onCallBusy(SipSession session) { + Log.d(TAG, "sip call busy: " + session); + Listener listener = mListener; + if (listener != null) { + try { + listener.onCallBusy(SipAudioCall.this); + } catch (Throwable t) { + Log.i(TAG, "onCallBusy(): " + t); + } + } + close(false); + } + + @Override + public void onCallChangeFailed(SipSession session, int errorCode, + String message) { + Log.d(TAG, "sip call change failed: " + message); + mErrorCode = errorCode; + mErrorMessage = message; + Listener listener = mListener; + if (listener != null) { + try { + listener.onError(SipAudioCall.this, mErrorCode, + message); + } catch (Throwable t) { + Log.i(TAG, "onCallBusy(): " + t); + } + } + } + + @Override + public void onError(SipSession session, int errorCode, + String message) { + SipAudioCall.this.onError(errorCode, message); + } + + @Override + public void onRegistering(SipSession session) { + // irrelevant + } + + @Override + public void onRegistrationTimeout(SipSession session) { + // irrelevant + } + + @Override + public void onRegistrationFailed(SipSession session, int errorCode, + String message) { + // irrelevant + } + + @Override + public void onRegistrationDone(SipSession session, int duration) { + // irrelevant + } + }; + } + + private void onError(int errorCode, String message) { + Log.d(TAG, "sip session error: " + + SipErrorCode.toString(errorCode) + ": " + message); + mErrorCode = errorCode; + mErrorMessage = message; + Listener listener = mListener; + if (listener != null) { + try { + listener.onError(this, errorCode, message); + } catch (Throwable t) { + Log.i(TAG, "onError(): " + t); + } + } + synchronized (this) { + if ((errorCode == SipErrorCode.DATA_CONNECTION_LOST) + || !isInCall()) { + close(true); + } + } + } /** * Attaches an incoming call to this call object. * * @param session the session that receives the incoming call * @param sessionDescription the session description of the incoming call + * @throws SipException if the SIP service fails to attach this object to + * the session + */ + public synchronized void attachCall(SipSession session, + String sessionDescription) throws SipException { + mSipSession = session; + mPeerSd = sessionDescription; + Log.v(TAG, "attachCall()" + mPeerSd); + try { + session.setListener(createListener()); + + if (getState() == SipSession.State.INCOMING_CALL) startRinging(); + } catch (Throwable e) { + Log.e(TAG, "attachCall()", e); + throwSipException(e); + } + } + + /** + * Initiates an audio call to the specified profile. The attempt will be + * timed out if the call is not established within {@code timeout} seconds + * and {@code Listener.onError(SipAudioCall, SipErrorCode.TIME_OUT, String)} + * will be called. + * + * @param callee the SIP profile to make the call to + * @param sipManager the {@link SipManager} object to help make call with + * @param timeout the timeout value in seconds. Default value (defined by + * SIP protocol) is used if {@code timeout} is zero or negative. + * @see Listener.onError + * @throws SipException if the SIP service fails to create a session for the + * call */ - void attachCall(ISipSession session, String sessionDescription) - throws SipException; + public synchronized void makeCall(SipProfile peerProfile, + SipManager sipManager, int timeout) throws SipException { + SipSession s = mSipSession = sipManager.createSipSession( + mLocalProfile, createListener()); + if (s == null) { + throw new SipException( + "Failed to create SipSession; network available?"); + } + try { + mAudioStream = new AudioStream(InetAddress.getByName(getLocalIp())); + s.makeCall(peerProfile, createOffer().encode(), timeout); + } catch (IOException e) { + throw new SipException("makeCall()", e); + } + } - /** Ends a call. */ - void endCall() throws SipException; + /** + * Ends a call. + * @throws SipException if the SIP service fails to end the call + */ + public synchronized void endCall() throws SipException { + stopRinging(); + stopCall(RELEASE_SOCKET); + mInCall = false; + + // perform the above local ops first and then network op + if (mSipSession != null) mSipSession.endCall(); + } /** * Puts a call on hold. When succeeds, {@link Listener#onCallHeld} is @@ -196,10 +573,19 @@ public interface SipAudioCall { * {@code Listener.onError(SipAudioCall, SipErrorCode.TIME_OUT, String)} * will be called. * - * @param timeout the timeout value in seconds + * @param timeout the timeout value in seconds. Default value (defined by + * SIP protocol) is used if {@code timeout} is zero or negative. * @see Listener.onError + * @throws SipException if the SIP service fails to hold the call */ - void holdCall(int timeout) throws SipException; + public synchronized void holdCall(int timeout) throws SipException { + if (mHold) return; + mSipSession.changeCall(createHoldOffer().encode(), timeout); + mHold = true; + + AudioGroup audioGroup = getAudioGroup(); + if (audioGroup != null) audioGroup.setMode(AudioGroup.MODE_ON_HOLD); + } /** * Answers a call. The attempt will be timed out if the call is not @@ -207,10 +593,20 @@ public interface SipAudioCall { * {@code Listener.onError(SipAudioCall, SipErrorCode.TIME_OUT, String)} * will be called. * - * @param timeout the timeout value in seconds + * @param timeout the timeout value in seconds. Default value (defined by + * SIP protocol) is used if {@code timeout} is zero or negative. * @see Listener.onError + * @throws SipException if the SIP service fails to answer the call */ - void answerCall(int timeout) throws SipException; + public synchronized void answerCall(int timeout) throws SipException { + stopRinging(); + try { + mAudioStream = new AudioStream(InetAddress.getByName(getLocalIp())); + mSipSession.answerCall(createAnswer(mPeerSd).encode(), timeout); + } catch (IOException e) { + throw new SipException("answerCall()", e); + } + } /** * Continues a call that's on hold. When succeeds, @@ -219,45 +615,191 @@ public interface SipAudioCall { * {@code Listener.onError(SipAudioCall, SipErrorCode.TIME_OUT, String)} * will be called. * - * @param timeout the timeout value in seconds + * @param timeout the timeout value in seconds. Default value (defined by + * SIP protocol) is used if {@code timeout} is zero or negative. * @see Listener.onError + * @throws SipException if the SIP service fails to unhold the call */ - void continueCall(int timeout) throws SipException; + public synchronized void continueCall(int timeout) throws SipException { + if (!mHold) return; + mSipSession.changeCall(createContinueOffer().encode(), timeout); + mHold = false; + AudioGroup audioGroup = getAudioGroup(); + if (audioGroup != null) audioGroup.setMode(AudioGroup.MODE_NORMAL); + } - /** Puts the device to speaker mode. */ - void setSpeakerMode(boolean speakerMode); + private SimpleSessionDescription createOffer() { + SimpleSessionDescription offer = + new SimpleSessionDescription(mSessionId, getLocalIp()); + AudioCodec[] codecs = AudioCodec.getCodecs(); + Media media = offer.newMedia( + "audio", mAudioStream.getLocalPort(), 1, "RTP/AVP"); + for (AudioCodec codec : AudioCodec.getCodecs()) { + media.setRtpPayload(codec.type, codec.rtpmap, codec.fmtp); + } + media.setRtpPayload(127, "telephone-event/8000", "0-15"); + return offer; + } - /** Toggles mute. */ - void toggleMute(); + private SimpleSessionDescription createAnswer(String offerSd) { + SimpleSessionDescription offer = + new SimpleSessionDescription(offerSd); + SimpleSessionDescription answer = + new SimpleSessionDescription(mSessionId, getLocalIp()); + AudioCodec codec = null; + for (Media media : offer.getMedia()) { + if ((codec == null) && (media.getPort() > 0) + && "audio".equals(media.getType()) + && "RTP/AVP".equals(media.getProtocol())) { + // Find the first audio codec we supported. + for (int type : media.getRtpPayloadTypes()) { + codec = AudioCodec.getCodec(type, media.getRtpmap(type), + media.getFmtp(type)); + if (codec != null) { + break; + } + } + if (codec != null) { + Media reply = answer.newMedia( + "audio", mAudioStream.getLocalPort(), 1, "RTP/AVP"); + reply.setRtpPayload(codec.type, codec.rtpmap, codec.fmtp); - /** - * Checks if the call is on hold. - * - * @return true if the call is on hold - */ - boolean isOnHold(); + // Check if DTMF is supported in the same media. + for (int type : media.getRtpPayloadTypes()) { + String rtpmap = media.getRtpmap(type); + if ((type != codec.type) && (rtpmap != null) + && rtpmap.startsWith("telephone-event")) { + reply.setRtpPayload( + type, rtpmap, media.getFmtp(type)); + } + } + + // Handle recvonly and sendonly. + if (media.getAttribute("recvonly") != null) { + answer.setAttribute("sendonly", ""); + } else if(media.getAttribute("sendonly") != null) { + answer.setAttribute("recvonly", ""); + } else if(offer.getAttribute("recvonly") != null) { + answer.setAttribute("sendonly", ""); + } else if(offer.getAttribute("sendonly") != null) { + answer.setAttribute("recvonly", ""); + } + continue; + } + } + // Reject the media. + Media reply = answer.newMedia( + media.getType(), 0, 1, media.getProtocol()); + for (String format : media.getFormats()) { + reply.setFormat(format, null); + } + } + if (codec == null) { + throw new IllegalStateException("Reject SDP: no suitable codecs"); + } + return answer; + } + + private SimpleSessionDescription createHoldOffer() { + SimpleSessionDescription offer = createContinueOffer(); + offer.setAttribute("sendonly", ""); + return offer; + } + + private SimpleSessionDescription createContinueOffer() { + SimpleSessionDescription offer = + new SimpleSessionDescription(mSessionId, getLocalIp()); + Media media = offer.newMedia( + "audio", mAudioStream.getLocalPort(), 1, "RTP/AVP"); + AudioCodec codec = mAudioStream.getCodec(); + media.setRtpPayload(codec.type, codec.rtpmap, codec.fmtp); + int dtmfType = mAudioStream.getDtmfType(); + if (dtmfType != -1) { + media.setRtpPayload(dtmfType, "telephone-event/8000", "0-15"); + } + return offer; + } + + private void grabWifiHighPerfLock() { + /* not available in master yet + if (mWifiHighPerfLock == null) { + Log.v(TAG, "acquire wifi high perf lock"); + mWifiHighPerfLock = ((WifiManager) + mContext.getSystemService(Context.WIFI_SERVICE)) + .createWifiLock(WifiManager.WIFI_MODE_FULL_HIGH_PERF, TAG); + mWifiHighPerfLock.acquire(); + } + */ + } + + private void releaseWifiHighPerfLock() { + if (mWifiHighPerfLock != null) { + Log.v(TAG, "release wifi high perf lock"); + mWifiHighPerfLock.release(); + mWifiHighPerfLock = null; + } + } + + private boolean isWifiOn() { + return (mWm.getConnectionInfo().getBSSID() == null) ? false : true; + } + + /** Toggles mute. */ + public synchronized void toggleMute() { + AudioGroup audioGroup = getAudioGroup(); + if (audioGroup != null) { + audioGroup.setMode( + mMuted ? AudioGroup.MODE_NORMAL : AudioGroup.MODE_MUTED); + mMuted = !mMuted; + } + } /** * Checks if the call is muted. * * @return true if the call is muted */ - boolean isMuted(); + public synchronized boolean isMuted() { + return mMuted; + } + + /** Puts the device to speaker mode. */ + public synchronized void setSpeakerMode(boolean speakerMode) { + ((AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE)) + .setSpeakerphoneOn(speakerMode); + } /** - * Sends a DTMF code. + * Sends a DTMF code. According to RFC2833, event 0--9 maps to decimal + * value 0--9, '*' to 10, '#' to 11, event 'A'--'D' to 12--15, and event + * flash to 16. Currently, event flash is not supported. * - * @param code the DTMF code to send + * @param code the DTMF code to send. Value 0 to 15 (inclusive) are valid + * inputs. + * @see http://tools.ietf.org/html/rfc2833 */ - void sendDtmf(int code); + public void sendDtmf(int code) { + sendDtmf(code, null); + } /** - * Sends a DTMF code. + * Sends a DTMF code. According to RFC2833, event 0--9 maps to decimal + * value 0--9, '*' to 10, '#' to 11, event 'A'--'D' to 12--15, and event + * flash to 16. Currently, event flash is not supported. * - * @param code the DTMF code to send + * @param code the DTMF code to send. Value 0 to 15 (inclusive) are valid + * inputs. * @param result the result message to send when done */ - void sendDtmf(int code, Message result); + public synchronized void sendDtmf(int code, Message result) { + AudioGroup audioGroup = getAudioGroup(); + if ((audioGroup != null) && (mSipSession != null) + && (SipSession.State.IN_CALL == getState())) { + Log.v(TAG, "send DTMF: " + code); + audioGroup.sendDtmf(code); + } + if (result != null) result.sendToTarget(); + } /** * Gets the {@link AudioStream} object used in this call. The object @@ -268,8 +810,11 @@ public interface SipAudioCall { * * @return the {@link AudioStream} object or null if the RTP stream has not * yet been set up + * @hide */ - AudioStream getAudioStream(); + public synchronized AudioStream getAudioStream() { + return mAudioStream; + } /** * Gets the {@link AudioGroup} object which the {@link AudioStream} object @@ -283,8 +828,12 @@ public interface SipAudioCall { * @return the {@link AudioGroup} object or null if the RTP stream has not * yet been set up * @see #getAudioStream + * @hide */ - AudioGroup getAudioGroup(); + public synchronized AudioGroup getAudioGroup() { + if (mAudioGroup != null) return mAudioGroup; + return ((mAudioStream == null) ? null : mAudioStream.getGroup()); + } /** * Sets the {@link AudioGroup} object which the {@link AudioStream} object @@ -292,56 +841,214 @@ public interface SipAudioCall { * will be dynamically created when needed. * * @see #getAudioStream + * @hide */ - void setAudioGroup(AudioGroup audioGroup); + public synchronized void setAudioGroup(AudioGroup group) { + if ((mAudioStream != null) && (mAudioStream.getGroup() != null)) { + mAudioStream.join(group); + } + mAudioGroup = group; + } /** - * Checks if the call is established. - * - * @return true if the call is established + * Starts the audio for the established call. This method should be called + * after {@link Listener#onCallEstablished} is called. */ - boolean isInCall(); + public void startAudio() { + try { + startAudioInternal(); + } catch (UnknownHostException e) { + onError(SipErrorCode.PEER_NOT_REACHABLE, e.getMessage()); + } catch (Throwable e) { + onError(SipErrorCode.CLIENT_ERROR, e.getMessage()); + } + } - /** - * Gets the local SIP profile. - * - * @return the local SIP profile - */ - SipProfile getLocalProfile(); + private synchronized void startAudioInternal() throws UnknownHostException { + if (mPeerSd == null) { + Log.v(TAG, "startAudioInternal() mPeerSd = null"); + throw new IllegalStateException("mPeerSd = null"); + } - /** - * Gets the peer's SIP profile. - * - * @return the peer's SIP profile - */ - SipProfile getPeerProfile(); + stopCall(DONT_RELEASE_SOCKET); + mInCall = true; - /** - * Gets the state of the {@link ISipSession} that carries this call. - * The value returned must be one of the states in {@link SipSessionState}. - * - * @return the session state - */ - int getState(); + // Run exact the same logic in createAnswer() to setup mAudioStream. + SimpleSessionDescription offer = + new SimpleSessionDescription(mPeerSd); + AudioStream stream = mAudioStream; + AudioCodec codec = null; + for (Media media : offer.getMedia()) { + if ((codec == null) && (media.getPort() > 0) + && "audio".equals(media.getType()) + && "RTP/AVP".equals(media.getProtocol())) { + // Find the first audio codec we supported. + for (int type : media.getRtpPayloadTypes()) { + codec = AudioCodec.getCodec( + type, media.getRtpmap(type), media.getFmtp(type)); + if (codec != null) { + break; + } + } + + if (codec != null) { + // Associate with the remote host. + String address = media.getAddress(); + if (address == null) { + address = offer.getAddress(); + } + stream.associate(InetAddress.getByName(address), + media.getPort()); + + stream.setDtmfType(-1); + stream.setCodec(codec); + // Check if DTMF is supported in the same media. + for (int type : media.getRtpPayloadTypes()) { + String rtpmap = media.getRtpmap(type); + if ((type != codec.type) && (rtpmap != null) + && rtpmap.startsWith("telephone-event")) { + stream.setDtmfType(type); + } + } + + // Handle recvonly and sendonly. + if (mHold) { + stream.setMode(RtpStream.MODE_NORMAL); + } else if (media.getAttribute("recvonly") != null) { + stream.setMode(RtpStream.MODE_SEND_ONLY); + } else if(media.getAttribute("sendonly") != null) { + stream.setMode(RtpStream.MODE_RECEIVE_ONLY); + } else if(offer.getAttribute("recvonly") != null) { + stream.setMode(RtpStream.MODE_SEND_ONLY); + } else if(offer.getAttribute("sendonly") != null) { + stream.setMode(RtpStream.MODE_RECEIVE_ONLY); + } else { + stream.setMode(RtpStream.MODE_NORMAL); + } + break; + } + } + } + if (codec == null) { + throw new IllegalStateException("Reject SDP: no suitable codecs"); + } + + if (isWifiOn()) grabWifiHighPerfLock(); + + if (!mHold) { + /* The recorder volume will be very low if the device is in + * IN_CALL mode. Therefore, we have to set the mode to NORMAL + * in order to have the normal microphone level. + */ + ((AudioManager) mContext.getSystemService + (Context.AUDIO_SERVICE)) + .setMode(AudioManager.MODE_NORMAL); + } + + // AudioGroup logic: + AudioGroup audioGroup = getAudioGroup(); + if (mHold) { + if (audioGroup != null) { + audioGroup.setMode(AudioGroup.MODE_ON_HOLD); + } + // don't create an AudioGroup here; doing so will fail if + // there's another AudioGroup out there that's active + } else { + if (audioGroup == null) audioGroup = new AudioGroup(); + stream.join(audioGroup); + if (mMuted) { + audioGroup.setMode(AudioGroup.MODE_MUTED); + } else { + audioGroup.setMode(AudioGroup.MODE_NORMAL); + } + } + } + + private void stopCall(boolean releaseSocket) { + Log.d(TAG, "stop audiocall"); + releaseWifiHighPerfLock(); + if (mAudioStream != null) { + mAudioStream.join(null); + + if (releaseSocket) { + mAudioStream.release(); + mAudioStream = null; + } + } + } + + private String getLocalIp() { + return mSipSession.getLocalIp(); + } - /** - * Gets the {@link ISipSession} that carries this call. - * - * @return the session object that carries this call - */ - ISipSession getSipSession(); /** * Enables/disables the ring-back tone. * * @param enabled true to enable; false to disable */ - void setRingbackToneEnabled(boolean enabled); + public synchronized void setRingbackToneEnabled(boolean enabled) { + mRingbackToneEnabled = enabled; + } /** * Enables/disables the ring tone. * * @param enabled true to enable; false to disable */ - void setRingtoneEnabled(boolean enabled); + public synchronized void setRingtoneEnabled(boolean enabled) { + mRingtoneEnabled = enabled; + } + + private void startRingbackTone() { + if (!mRingbackToneEnabled) return; + if (mRingbackTone == null) { + // The volume relative to other sounds in the stream + int toneVolume = 80; + mRingbackTone = new ToneGenerator( + AudioManager.STREAM_VOICE_CALL, toneVolume); + } + mRingbackTone.startTone(ToneGenerator.TONE_CDMA_LOW_PBX_L); + } + + private void stopRingbackTone() { + if (mRingbackTone != null) { + mRingbackTone.stopTone(); + mRingbackTone.release(); + mRingbackTone = null; + } + } + + private void startRinging() { + if (!mRingtoneEnabled) return; + ((Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE)) + .vibrate(new long[] {0, 1000, 1000}, 1); + AudioManager am = (AudioManager) + mContext.getSystemService(Context.AUDIO_SERVICE); + if (am.getStreamVolume(AudioManager.STREAM_RING) > 0) { + String ringtoneUri = + Settings.System.DEFAULT_RINGTONE_URI.toString(); + mRingtone = RingtoneManager.getRingtone(mContext, + Uri.parse(ringtoneUri)); + mRingtone.play(); + } + } + + private void stopRinging() { + ((Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE)) + .cancel(); + if (mRingtone != null) mRingtone.stop(); + } + + private void throwSipException(Throwable throwable) throws SipException { + if (throwable instanceof SipException) { + throw (SipException) throwable; + } else { + throw new SipException("", throwable); + } + } + + private SipProfile getPeerProfile(SipSession session) { + return session.getPeerProfile(); + } } diff --git a/voip/java/android/net/sip/SipAudioCallImpl.java b/voip/java/android/net/sip/SipAudioCallImpl.java deleted file mode 100644 index 5eecc05..0000000 --- a/voip/java/android/net/sip/SipAudioCallImpl.java +++ /dev/null @@ -1,738 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net.sip; - -import android.content.Context; -import android.media.AudioManager; -import android.media.Ringtone; -import android.media.RingtoneManager; -import android.media.ToneGenerator; -import android.net.Uri; -import android.net.rtp.AudioCodec; -import android.net.rtp.AudioGroup; -import android.net.rtp.AudioStream; -import android.net.rtp.RtpStream; -import android.net.sip.SimpleSessionDescription.Media; -import android.net.wifi.WifiManager; -import android.os.Message; -import android.os.RemoteException; -import android.os.Vibrator; -import android.provider.Settings; -import android.util.Log; - -import java.io.IOException; -import java.net.InetAddress; -import java.net.UnknownHostException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** - * Class that handles an audio call over SIP. - */ -/** @hide */ -public class SipAudioCallImpl extends SipSessionAdapter - implements SipAudioCall { - private static final String TAG = SipAudioCallImpl.class.getSimpleName(); - private static final boolean RELEASE_SOCKET = true; - private static final boolean DONT_RELEASE_SOCKET = false; - private static final int SESSION_TIMEOUT = 5; // in seconds - - private Context mContext; - private SipProfile mLocalProfile; - private SipAudioCall.Listener mListener; - private ISipSession mSipSession; - - private long mSessionId = System.currentTimeMillis(); - private String mPeerSd; - - private AudioStream mAudioStream; - private AudioGroup mAudioGroup; - - private boolean mInCall = false; - private boolean mMuted = false; - private boolean mHold = false; - - private boolean mRingbackToneEnabled = true; - private boolean mRingtoneEnabled = true; - private Ringtone mRingtone; - private ToneGenerator mRingbackTone; - - private SipProfile mPendingCallRequest; - - private int mErrorCode = SipErrorCode.NO_ERROR; - private String mErrorMessage; - - public SipAudioCallImpl(Context context, SipProfile localProfile) { - mContext = context; - mLocalProfile = localProfile; - } - - public void setListener(SipAudioCall.Listener listener) { - setListener(listener, false); - } - - public void setListener(SipAudioCall.Listener listener, - boolean callbackImmediately) { - mListener = listener; - try { - if ((listener == null) || !callbackImmediately) { - // do nothing - } else if (mErrorCode != SipErrorCode.NO_ERROR) { - listener.onError(this, mErrorCode, mErrorMessage); - } else if (mInCall) { - if (mHold) { - listener.onCallHeld(this); - } else { - listener.onCallEstablished(this); - } - } else { - int state = getState(); - switch (state) { - case SipSessionState.READY_TO_CALL: - listener.onReadyToCall(this); - break; - case SipSessionState.INCOMING_CALL: - listener.onRinging(this, getPeerProfile(mSipSession)); - break; - case SipSessionState.OUTGOING_CALL: - listener.onCalling(this); - break; - case SipSessionState.OUTGOING_CALL_RING_BACK: - listener.onRingingBack(this); - break; - } - } - } catch (Throwable t) { - Log.e(TAG, "setListener()", t); - } - } - - public synchronized boolean isInCall() { - return mInCall; - } - - public synchronized boolean isOnHold() { - return mHold; - } - - public void close() { - close(true); - } - - private synchronized void close(boolean closeRtp) { - if (closeRtp) stopCall(RELEASE_SOCKET); - stopRingbackTone(); - stopRinging(); - - mInCall = false; - mHold = false; - mSessionId = System.currentTimeMillis(); - mErrorCode = SipErrorCode.NO_ERROR; - mErrorMessage = null; - - if (mSipSession != null) { - try { - mSipSession.setListener(null); - } catch (RemoteException e) { - // don't care - } - mSipSession = null; - } - } - - public synchronized SipProfile getLocalProfile() { - return mLocalProfile; - } - - public synchronized SipProfile getPeerProfile() { - try { - return (mSipSession == null) ? null : mSipSession.getPeerProfile(); - } catch (RemoteException e) { - return null; - } - } - - public synchronized int getState() { - if (mSipSession == null) return SipSessionState.READY_TO_CALL; - try { - return mSipSession.getState(); - } catch (RemoteException e) { - return SipSessionState.REMOTE_ERROR; - } - } - - - public synchronized ISipSession getSipSession() { - return mSipSession; - } - - @Override - public void onCalling(ISipSession session) { - Log.d(TAG, "calling... " + session); - Listener listener = mListener; - if (listener != null) { - try { - listener.onCalling(this); - } catch (Throwable t) { - Log.e(TAG, "onCalling()", t); - } - } - } - - @Override - public void onRingingBack(ISipSession session) { - Log.d(TAG, "sip call ringing back: " + session); - if (!mInCall) startRingbackTone(); - Listener listener = mListener; - if (listener != null) { - try { - listener.onRingingBack(this); - } catch (Throwable t) { - Log.e(TAG, "onRingingBack()", t); - } - } - } - - @Override - public synchronized void onRinging(ISipSession session, - SipProfile peerProfile, String sessionDescription) { - try { - if ((mSipSession == null) || !mInCall - || !session.getCallId().equals(mSipSession.getCallId())) { - // should not happen - session.endCall(); - return; - } - - // session changing request - try { - String answer = createAnswer(sessionDescription).encode(); - mSipSession.answerCall(answer, SESSION_TIMEOUT); - } catch (Throwable e) { - Log.e(TAG, "onRinging()", e); - session.endCall(); - } - } catch (RemoteException e) { - Log.e(TAG, "onRinging()", e); - } - } - - @Override - public void onCallEstablished(ISipSession session, - String sessionDescription) { - stopRingbackTone(); - stopRinging(); - mPeerSd = sessionDescription; - Log.v(TAG, "onCallEstablished()" + mPeerSd); - - Listener listener = mListener; - if (listener != null) { - try { - if (mHold) { - listener.onCallHeld(this); - } else { - listener.onCallEstablished(this); - } - } catch (Throwable t) { - Log.e(TAG, "onCallEstablished()", t); - } - } - } - - @Override - public void onCallEnded(ISipSession session) { - Log.d(TAG, "sip call ended: " + session); - Listener listener = mListener; - if (listener != null) { - try { - listener.onCallEnded(this); - } catch (Throwable t) { - Log.e(TAG, "onCallEnded()", t); - } - } - close(); - } - - @Override - public void onCallBusy(ISipSession session) { - Log.d(TAG, "sip call busy: " + session); - Listener listener = mListener; - if (listener != null) { - try { - listener.onCallBusy(this); - } catch (Throwable t) { - Log.e(TAG, "onCallBusy()", t); - } - } - close(false); - } - - @Override - public void onCallChangeFailed(ISipSession session, int errorCode, - String message) { - Log.d(TAG, "sip call change failed: " + message); - mErrorCode = errorCode; - mErrorMessage = message; - Listener listener = mListener; - if (listener != null) { - try { - listener.onError(this, mErrorCode, message); - } catch (Throwable t) { - Log.e(TAG, "onCallBusy()", t); - } - } - } - - @Override - public void onError(ISipSession session, int errorCode, String message) { - Log.d(TAG, "sip session error: " + SipErrorCode.toString(errorCode) - + ": " + message); - mErrorCode = errorCode; - mErrorMessage = message; - Listener listener = mListener; - if (listener != null) { - try { - listener.onError(this, errorCode, message); - } catch (Throwable t) { - Log.e(TAG, "onError()", t); - } - } - synchronized (this) { - if ((errorCode == SipErrorCode.DATA_CONNECTION_LOST) - || !isInCall()) { - close(true); - } - } - } - - public synchronized void attachCall(ISipSession session, - String sessionDescription) throws SipException { - mSipSession = session; - mPeerSd = sessionDescription; - Log.v(TAG, "attachCall()" + mPeerSd); - try { - session.setListener(this); - if (getState() == SipSessionState.INCOMING_CALL) startRinging(); - } catch (Throwable e) { - Log.e(TAG, "attachCall()", e); - throwSipException(e); - } - } - - public synchronized void makeCall(SipProfile peerProfile, - SipManager sipManager, int timeout) throws SipException { - try { - mSipSession = sipManager.createSipSession(mLocalProfile, this); - if (mSipSession == null) { - throw new SipException( - "Failed to create SipSession; network available?"); - } - mAudioStream = new AudioStream(InetAddress.getByName(getLocalIp())); - mSipSession.makeCall(peerProfile, createOffer().encode(), timeout); - } catch (Throwable e) { - if (e instanceof SipException) { - throw (SipException) e; - } else { - throwSipException(e); - } - } - } - - public synchronized void endCall() throws SipException { - try { - stopRinging(); - stopCall(RELEASE_SOCKET); - mInCall = false; - - // perform the above local ops first and then network op - if (mSipSession != null) mSipSession.endCall(); - } catch (Throwable e) { - throwSipException(e); - } - } - - public synchronized void answerCall(int timeout) throws SipException { - try { - stopRinging(); - mAudioStream = new AudioStream(InetAddress.getByName(getLocalIp())); - mSipSession.answerCall(createAnswer(mPeerSd).encode(), timeout); - } catch (Throwable e) { - Log.e(TAG, "answerCall()", e); - throwSipException(e); - } - } - - public synchronized void holdCall(int timeout) throws SipException { - if (mHold) return; - try { - mSipSession.changeCall(createHoldOffer().encode(), timeout); - } catch (Throwable e) { - throwSipException(e); - } - mHold = true; - AudioGroup audioGroup = getAudioGroup(); - if (audioGroup != null) audioGroup.setMode(AudioGroup.MODE_ON_HOLD); - } - - public synchronized void continueCall(int timeout) throws SipException { - if (!mHold) return; - try { - mSipSession.changeCall(createContinueOffer().encode(), timeout); - } catch (Throwable e) { - throwSipException(e); - } - mHold = false; - AudioGroup audioGroup = getAudioGroup(); - if (audioGroup != null) audioGroup.setMode(AudioGroup.MODE_NORMAL); - } - - private SimpleSessionDescription createOffer() { - SimpleSessionDescription offer = - new SimpleSessionDescription(mSessionId, getLocalIp()); - AudioCodec[] codecs = AudioCodec.getCodecs(); - Media media = offer.newMedia( - "audio", mAudioStream.getLocalPort(), 1, "RTP/AVP"); - for (AudioCodec codec : AudioCodec.getCodecs()) { - media.setRtpPayload(codec.type, codec.rtpmap, codec.fmtp); - } - media.setRtpPayload(127, "telephone-event/8000", "0-15"); - return offer; - } - - private SimpleSessionDescription createAnswer(String offerSd) { - SimpleSessionDescription offer = - new SimpleSessionDescription(offerSd); - SimpleSessionDescription answer = - new SimpleSessionDescription(mSessionId, getLocalIp()); - AudioCodec codec = null; - for (Media media : offer.getMedia()) { - if ((codec == null) && (media.getPort() > 0) - && "audio".equals(media.getType()) - && "RTP/AVP".equals(media.getProtocol())) { - // Find the first audio codec we supported. - for (int type : media.getRtpPayloadTypes()) { - codec = AudioCodec.getCodec(type, media.getRtpmap(type), - media.getFmtp(type)); - if (codec != null) { - break; - } - } - if (codec != null) { - Media reply = answer.newMedia( - "audio", mAudioStream.getLocalPort(), 1, "RTP/AVP"); - reply.setRtpPayload(codec.type, codec.rtpmap, codec.fmtp); - - // Check if DTMF is supported in the same media. - for (int type : media.getRtpPayloadTypes()) { - String rtpmap = media.getRtpmap(type); - if ((type != codec.type) && (rtpmap != null) - && rtpmap.startsWith("telephone-event")) { - reply.setRtpPayload( - type, rtpmap, media.getFmtp(type)); - } - } - - // Handle recvonly and sendonly. - if (media.getAttribute("recvonly") != null) { - answer.setAttribute("sendonly", ""); - } else if(media.getAttribute("sendonly") != null) { - answer.setAttribute("recvonly", ""); - } else if(offer.getAttribute("recvonly") != null) { - answer.setAttribute("sendonly", ""); - } else if(offer.getAttribute("sendonly") != null) { - answer.setAttribute("recvonly", ""); - } - continue; - } - } - // Reject the media. - Media reply = answer.newMedia( - media.getType(), 0, 1, media.getProtocol()); - for (String format : media.getFormats()) { - reply.setFormat(format, null); - } - } - if (codec == null) { - throw new IllegalStateException("Reject SDP: no suitable codecs"); - } - return answer; - } - - private SimpleSessionDescription createHoldOffer() { - SimpleSessionDescription offer = createContinueOffer(); - offer.setAttribute("sendonly", ""); - return offer; - } - - private SimpleSessionDescription createContinueOffer() { - SimpleSessionDescription offer = - new SimpleSessionDescription(mSessionId, getLocalIp()); - Media media = offer.newMedia( - "audio", mAudioStream.getLocalPort(), 1, "RTP/AVP"); - AudioCodec codec = mAudioStream.getCodec(); - media.setRtpPayload(codec.type, codec.rtpmap, codec.fmtp); - int dtmfType = mAudioStream.getDtmfType(); - if (dtmfType != -1) { - media.setRtpPayload(dtmfType, "telephone-event/8000", "0-15"); - } - return offer; - } - - public synchronized void toggleMute() { - AudioGroup audioGroup = getAudioGroup(); - if (audioGroup != null) { - audioGroup.setMode( - mMuted ? AudioGroup.MODE_NORMAL : AudioGroup.MODE_MUTED); - mMuted = !mMuted; - } - } - - public synchronized boolean isMuted() { - return mMuted; - } - - public synchronized void setSpeakerMode(boolean speakerMode) { - ((AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE)) - .setSpeakerphoneOn(speakerMode); - } - - public void sendDtmf(int code) { - sendDtmf(code, null); - } - - public synchronized void sendDtmf(int code, Message result) { - AudioGroup audioGroup = getAudioGroup(); - if ((audioGroup != null) && (mSipSession != null) - && (SipSessionState.IN_CALL == getState())) { - Log.v(TAG, "send DTMF: " + code); - audioGroup.sendDtmf(code); - } - if (result != null) result.sendToTarget(); - } - - public synchronized AudioStream getAudioStream() { - return mAudioStream; - } - - public synchronized AudioGroup getAudioGroup() { - if (mAudioGroup != null) return mAudioGroup; - return ((mAudioStream == null) ? null : mAudioStream.getGroup()); - } - - public synchronized void setAudioGroup(AudioGroup group) { - if ((mAudioStream != null) && (mAudioStream.getGroup() != null)) { - mAudioStream.join(group); - } - mAudioGroup = group; - } - - public void startAudio() { - try { - startAudioInternal(); - } catch (UnknownHostException e) { - onError(mSipSession, SipErrorCode.PEER_NOT_REACHABLE, - e.getMessage()); - } catch (Throwable e) { - onError(mSipSession, SipErrorCode.CLIENT_ERROR, - e.getMessage()); - } - } - - private synchronized void startAudioInternal() throws UnknownHostException { - if (mPeerSd == null) { - Log.v(TAG, "startAudioInternal() mPeerSd = null"); - throw new IllegalStateException("mPeerSd = null"); - } - - stopCall(DONT_RELEASE_SOCKET); - mInCall = true; - - // Run exact the same logic in createAnswer() to setup mAudioStream. - SimpleSessionDescription offer = - new SimpleSessionDescription(mPeerSd); - AudioStream stream = mAudioStream; - AudioCodec codec = null; - for (Media media : offer.getMedia()) { - if ((codec == null) && (media.getPort() > 0) - && "audio".equals(media.getType()) - && "RTP/AVP".equals(media.getProtocol())) { - // Find the first audio codec we supported. - for (int type : media.getRtpPayloadTypes()) { - codec = AudioCodec.getCodec( - type, media.getRtpmap(type), media.getFmtp(type)); - if (codec != null) { - break; - } - } - - if (codec != null) { - // Associate with the remote host. - String address = media.getAddress(); - if (address == null) { - address = offer.getAddress(); - } - stream.associate(InetAddress.getByName(address), - media.getPort()); - - stream.setDtmfType(-1); - stream.setCodec(codec); - // Check if DTMF is supported in the same media. - for (int type : media.getRtpPayloadTypes()) { - String rtpmap = media.getRtpmap(type); - if ((type != codec.type) && (rtpmap != null) - && rtpmap.startsWith("telephone-event")) { - stream.setDtmfType(type); - } - } - - // Handle recvonly and sendonly. - if (mHold) { - stream.setMode(RtpStream.MODE_NORMAL); - } else if (media.getAttribute("recvonly") != null) { - stream.setMode(RtpStream.MODE_SEND_ONLY); - } else if(media.getAttribute("sendonly") != null) { - stream.setMode(RtpStream.MODE_RECEIVE_ONLY); - } else if(offer.getAttribute("recvonly") != null) { - stream.setMode(RtpStream.MODE_SEND_ONLY); - } else if(offer.getAttribute("sendonly") != null) { - stream.setMode(RtpStream.MODE_RECEIVE_ONLY); - } else { - stream.setMode(RtpStream.MODE_NORMAL); - } - break; - } - } - } - if (codec == null) { - throw new IllegalStateException("Reject SDP: no suitable codecs"); - } - - if (!mHold) { - /* The recorder volume will be very low if the device is in - * IN_CALL mode. Therefore, we have to set the mode to NORMAL - * in order to have the normal microphone level. - */ - ((AudioManager) mContext.getSystemService - (Context.AUDIO_SERVICE)) - .setMode(AudioManager.MODE_NORMAL); - } - - // AudioGroup logic: - AudioGroup audioGroup = getAudioGroup(); - if (mHold) { - if (audioGroup != null) { - audioGroup.setMode(AudioGroup.MODE_ON_HOLD); - } - // don't create an AudioGroup here; doing so will fail if - // there's another AudioGroup out there that's active - } else { - if (audioGroup == null) audioGroup = new AudioGroup(); - mAudioStream.join(audioGroup); - if (mMuted) { - audioGroup.setMode(AudioGroup.MODE_MUTED); - } else { - audioGroup.setMode(AudioGroup.MODE_NORMAL); - } - } - } - - private void stopCall(boolean releaseSocket) { - Log.d(TAG, "stop audiocall"); - if (mAudioStream != null) { - mAudioStream.join(null); - - if (releaseSocket) { - mAudioStream.release(); - mAudioStream = null; - } - } - } - - private String getLocalIp() { - try { - return mSipSession.getLocalIp(); - } catch (RemoteException e) { - throw new IllegalStateException(e); - } - } - - public synchronized void setRingbackToneEnabled(boolean enabled) { - mRingbackToneEnabled = enabled; - } - - public synchronized void setRingtoneEnabled(boolean enabled) { - mRingtoneEnabled = enabled; - } - - private void startRingbackTone() { - if (!mRingbackToneEnabled) return; - if (mRingbackTone == null) { - // The volume relative to other sounds in the stream - int toneVolume = 80; - mRingbackTone = new ToneGenerator( - AudioManager.STREAM_VOICE_CALL, toneVolume); - } - mRingbackTone.startTone(ToneGenerator.TONE_CDMA_LOW_PBX_L); - } - - private void stopRingbackTone() { - if (mRingbackTone != null) { - mRingbackTone.stopTone(); - mRingbackTone.release(); - mRingbackTone = null; - } - } - - private void startRinging() { - if (!mRingtoneEnabled) return; - ((Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE)) - .vibrate(new long[] {0, 1000, 1000}, 1); - AudioManager am = (AudioManager) - mContext.getSystemService(Context.AUDIO_SERVICE); - if (am.getStreamVolume(AudioManager.STREAM_RING) > 0) { - String ringtoneUri = - Settings.System.DEFAULT_RINGTONE_URI.toString(); - mRingtone = RingtoneManager.getRingtone(mContext, - Uri.parse(ringtoneUri)); - mRingtone.play(); - } - } - - private void stopRinging() { - ((Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE)) - .cancel(); - if (mRingtone != null) mRingtone.stop(); - } - - private void throwSipException(Throwable throwable) throws SipException { - if (throwable instanceof SipException) { - throw (SipException) throwable; - } else { - throw new SipException("", throwable); - } - } - - private SipProfile getPeerProfile(ISipSession session) { - try { - return session.getPeerProfile(); - } catch (RemoteException e) { - return null; - } - } -} diff --git a/voip/java/android/net/sip/SipManager.java b/voip/java/android/net/sip/SipManager.java index 31768d7..5976a04 100644 --- a/voip/java/android/net/sip/SipManager.java +++ b/voip/java/android/net/sip/SipManager.java @@ -30,8 +30,9 @@ import java.text.ParseException; * The class provides API for various SIP related tasks. Specifically, the API * allows an application to: * <ul> - * <li>register a {@link SipProfile} to have the background SIP service listen - * to incoming calls and broadcast them with registered command string. See + * <li>open a {@link SipProfile} to get ready for making outbound calls or have + * the background SIP service listen to incoming calls and broadcast them + * with registered command string. See * {@link #open(SipProfile, String, SipRegistrationListener)}, * {@link #open(SipProfile)}, {@link #close}, {@link #isOpened} and * {@link #isRegistered}. It also facilitates handling of the incoming call @@ -40,39 +41,59 @@ import java.text.ParseException; * {@link #getOfferSessionDescription} and {@link #takeAudioCall}.</li> * <li>make/take SIP-based audio calls. See * {@link #makeAudioCall} and {@link #takeAudioCall}.</li> - * <li>register/unregister with a SIP service provider. See + * <li>register/unregister with a SIP service provider manually. See * {@link #register} and {@link #unregister}.</li> - * <li>process SIP events directly with a {@link ISipSession} created by + * <li>process SIP events directly with a {@link SipSession} created by * {@link #createSipSession}.</li> * </ul> * @hide */ public class SipManager { - /** @hide */ - public static final String SIP_INCOMING_CALL_ACTION = + /** + * Action string for the incoming call intent for the Phone app. + * Internal use only. + * @hide + */ + public static final String ACTION_SIP_INCOMING_CALL = "com.android.phone.SIP_INCOMING_CALL"; - /** @hide */ - public static final String SIP_ADD_PHONE_ACTION = + /** + * Action string for the add-phone intent. + * Internal use only. + * @hide + */ + public static final String ACTION_SIP_ADD_PHONE = "com.android.phone.SIP_ADD_PHONE"; - /** @hide */ - public static final String SIP_REMOVE_PHONE_ACTION = + /** + * Action string for the remove-phone intent. + * Internal use only. + * @hide + */ + public static final String ACTION_SIP_REMOVE_PHONE = "com.android.phone.SIP_REMOVE_PHONE"; - /** @hide */ - public static final String LOCAL_URI_KEY = "LOCAL SIPURI"; + /** + * Part of the ACTION_SIP_ADD_PHONE and ACTION_SIP_REMOVE_PHONE intents. + * Internal use only. + * @hide + */ + public static final String EXTRA_LOCAL_URI = "android:localSipUri"; - private static final String CALL_ID_KEY = "CallID"; - private static final String OFFER_SD_KEY = "OfferSD"; + /** Part of the incoming call intent. */ + public static final String EXTRA_CALL_ID = "android:sipCallID"; + + /** Part of the incoming call intent. */ + public static final String EXTRA_OFFER_SD = "android:sipOfferSD"; private ISipService mSipService; + private Context mContext; /** - * Gets a manager instance. Returns null if SIP API is not supported. + * Creates a manager instance. Returns null if SIP API is not supported. * - * @param context application context for checking if SIP API is supported + * @param context application context for creating the manager object * @return the manager instance or null if SIP API is not supported */ - public static SipManager getInstance(Context context) { - return (isApiSupported(context) ? new SipManager() : null); + public static SipManager newInstance(Context context) { + return (isApiSupported(context) ? new SipManager(context) : null); } /** @@ -80,7 +101,7 @@ public class SipManager { */ public static boolean isApiSupported(Context context) { return true; - /* + /* TODO: uncomment this before ship return context.getPackageManager().hasSystemFeature( PackageManager.FEATURE_SIP); */ @@ -91,7 +112,7 @@ public class SipManager { */ public static boolean isVoipSupported(Context context) { return true; - /* + /* TODO: uncomment this before ship return context.getPackageManager().hasSystemFeature( PackageManager.FEATURE_SIP_VOIP) && isApiSupported(context); */ @@ -105,23 +126,21 @@ public class SipManager { com.android.internal.R.bool.config_sip_wifi_only); } - private SipManager() { + private SipManager(Context context) { + mContext = context; createSipService(); } private void createSipService() { - if (mSipService != null) return; IBinder b = ServiceManager.getService(Context.SIP_SERVICE); mSipService = ISipService.Stub.asInterface(b); } /** - * Opens the profile for making calls and/or receiving calls. Subsequent - * SIP calls can be made through the default phone UI. The caller may also - * make subsequent calls through {@link #makeAudioCall}. - * If the receiving-call option is enabled in the profile, the SIP service - * will register the profile to the corresponding server periodically in - * order to receive calls from the server. + * Opens the profile for making calls. The caller may make subsequent calls + * through {@link #makeAudioCall}. If one also wants to receive calls on the + * profile, use {@link #open(SipProfile, String, SipRegistrationListener)} + * instead. * * @param localProfile the SIP profile to make calls from * @throws SipException if the profile contains incorrect settings or @@ -136,12 +155,11 @@ public class SipManager { } /** - * Opens the profile for making calls and/or receiving calls. Subsequent - * SIP calls can be made through the default phone UI. The caller may also - * make subsequent calls through {@link #makeAudioCall}. - * If the receiving-call option is enabled in the profile, the SIP service - * will register the profile to the corresponding server periodically in - * order to receive calls from the server. + * Opens the profile for making calls and/or receiving calls. The caller may + * make subsequent calls through {@link #makeAudioCall}. If the + * auto-registration option is enabled in the profile, the SIP service + * will register the profile to the corresponding SIP provider periodically + * in order to receive calls from the provider. * * @param localProfile the SIP profile to receive incoming calls for * @param incomingCallBroadcastAction the action to be broadcast when an @@ -195,7 +213,8 @@ public class SipManager { } /** - * Checks if the specified profile is enabled to receive calls. + * Checks if the specified profile is opened in the SIP service for + * making and/or receiving calls. * * @param localProfileUri the URI of the profile in question * @return true if the profile is enabled to receive calls @@ -210,11 +229,16 @@ public class SipManager { } /** - * Checks if the specified profile is registered to the server for - * receiving calls. + * Checks if the SIP service has successfully registered the profile to the + * SIP provider (specified in the profile) for receiving calls. Returning + * true from this method also implies the profile is opened + * ({@link #isOpened}). * * @param localProfileUri the URI of the profile in question - * @return true if the profile is registered to the server + * @return true if the profile is registered to the SIP provider; false if + * the profile has not been opened in the SIP service or the SIP + * service has not yet successfully registered the profile to the SIP + * provider * @throws SipException if calling the SIP service results in an error */ public boolean isRegistered(String localProfileUri) throws SipException { @@ -231,7 +255,6 @@ public class SipManager { * {@code SipAudioCall.Listener.onError(SipAudioCall, SipErrorCode.TIME_OUT, String)} * will be called. * - * @param context context to create a {@link SipAudioCall} object * @param localProfile the SIP profile to make the call from * @param peerProfile the SIP profile to make the call to * @param listener to listen to the call events from {@link SipAudioCall}; @@ -241,10 +264,10 @@ public class SipManager { * @throws SipException if calling the SIP service results in an error * @see SipAudioCall.Listener.onError */ - public SipAudioCall makeAudioCall(Context context, SipProfile localProfile, + public SipAudioCall makeAudioCall(SipProfile localProfile, SipProfile peerProfile, SipAudioCall.Listener listener, int timeout) throws SipException { - SipAudioCall call = new SipAudioCallImpl(context, localProfile); + SipAudioCall call = new SipAudioCall(mContext, localProfile); call.setListener(listener); call.makeCall(peerProfile, this, timeout); return call; @@ -257,7 +280,6 @@ public class SipManager { * {@code SipAudioCall.Listener.onError(SipAudioCall, SipErrorCode.TIME_OUT, String)} * will be called. * - * @param context context to create a {@link SipAudioCall} object * @param localProfileUri URI of the SIP profile to make the call from * @param peerProfileUri URI of the SIP profile to make the call to * @param listener to listen to the call events from {@link SipAudioCall}; @@ -267,11 +289,11 @@ public class SipManager { * @throws SipException if calling the SIP service results in an error * @see SipAudioCall.Listener.onError */ - public SipAudioCall makeAudioCall(Context context, String localProfileUri, + public SipAudioCall makeAudioCall(String localProfileUri, String peerProfileUri, SipAudioCall.Listener listener, int timeout) throws SipException { try { - return makeAudioCall(context, + return makeAudioCall( new SipProfile.Builder(localProfileUri).build(), new SipProfile.Builder(peerProfileUri).build(), listener, timeout); @@ -281,15 +303,14 @@ public class SipManager { } /** - * The method calls {@code takeAudioCall(context, incomingCallIntent, + * The method calls {@code takeAudioCall(incomingCallIntent, * listener, true}. * - * @see #takeAudioCall(Context, Intent, SipAudioCall.Listener, boolean) + * @see #takeAudioCall(Intent, SipAudioCall.Listener, boolean) */ - public SipAudioCall takeAudioCall(Context context, - Intent incomingCallIntent, SipAudioCall.Listener listener) - throws SipException { - return takeAudioCall(context, incomingCallIntent, listener, true); + public SipAudioCall takeAudioCall(Intent incomingCallIntent, + SipAudioCall.Listener listener) throws SipException { + return takeAudioCall(incomingCallIntent, listener, true); } /** @@ -298,16 +319,15 @@ public class SipManager { * {@link SipAudioCall.Listener#onRinging} * callback. * - * @param context context to create a {@link SipAudioCall} object * @param incomingCallIntent the incoming call broadcast intent * @param listener to listen to the call events from {@link SipAudioCall}; * can be null * @return a {@link SipAudioCall} object * @throws SipException if calling the SIP service results in an error */ - public SipAudioCall takeAudioCall(Context context, - Intent incomingCallIntent, SipAudioCall.Listener listener, - boolean ringtoneEnabled) throws SipException { + public SipAudioCall takeAudioCall(Intent incomingCallIntent, + SipAudioCall.Listener listener, boolean ringtoneEnabled) + throws SipException { if (incomingCallIntent == null) return null; String callId = getCallId(incomingCallIntent); @@ -324,10 +344,10 @@ public class SipManager { try { ISipSession session = mSipService.getPendingSession(callId); if (session == null) return null; - SipAudioCall call = new SipAudioCallImpl( - context, session.getLocalProfile()); + SipAudioCall call = new SipAudioCall( + mContext, session.getLocalProfile()); call.setRingtoneEnabled(ringtoneEnabled); - call.attachCall(session, offerSd); + call.attachCall(new SipSession(session), offerSd); call.setListener(listener); return call; } catch (Throwable t) { @@ -355,7 +375,7 @@ public class SipManager { * @return the call ID or null if the intent does not contain it */ public static String getCallId(Intent incomingCallIntent) { - return incomingCallIntent.getStringExtra(CALL_ID_KEY); + return incomingCallIntent.getStringExtra(EXTRA_CALL_ID); } /** @@ -367,30 +387,30 @@ public class SipManager { * have it */ public static String getOfferSessionDescription(Intent incomingCallIntent) { - return incomingCallIntent.getStringExtra(OFFER_SD_KEY); + return incomingCallIntent.getStringExtra(EXTRA_OFFER_SD); } /** * Creates an incoming call broadcast intent. * - * @param action the action string to broadcast * @param callId the call ID of the incoming call * @param sessionDescription the session description of the incoming call * @return the incoming call intent * @hide */ - public static Intent createIncomingCallBroadcast(String action, - String callId, String sessionDescription) { - Intent intent = new Intent(action); - intent.putExtra(CALL_ID_KEY, callId); - intent.putExtra(OFFER_SD_KEY, sessionDescription); + public static Intent createIncomingCallBroadcast(String callId, + String sessionDescription) { + Intent intent = new Intent(); + intent.putExtra(EXTRA_CALL_ID, callId); + intent.putExtra(EXTRA_OFFER_SD, sessionDescription); return intent; } /** - * Registers the profile to the corresponding server for receiving calls. - * {@link #open} is still needed to be called at least once in order for - * the SIP service to broadcast an intent when an incoming call is received. + * Manually registers the profile to the corresponding SIP provider for + * receiving calls. {@link #open(SipProfile, String, SipRegistrationListener)} + * is still needed to be called at least once in order for the SIP service + * to broadcast an intent when an incoming call is received. * * @param localProfile the SIP profile to register with * @param expiryTime registration expiration time (in seconds) @@ -409,8 +429,10 @@ public class SipManager { } /** - * Unregisters the profile from the corresponding server for not receiving - * further calls. + * Manually unregisters the profile from the corresponding SIP provider for + * stop receiving further calls. This may interference with the auto + * registration process in the SIP service if the auto-registration option + * in the profile is enabled. * * @param localProfile the SIP profile to register with * @param listener to listen to the registration events @@ -460,10 +482,11 @@ public class SipManager { * @param localProfile the SIP profile the session is associated with * @param listener to listen to SIP session events */ - public ISipSession createSipSession(SipProfile localProfile, - ISipSessionListener listener) throws SipException { + public SipSession createSipSession(SipProfile localProfile, + SipSession.Listener listener) throws SipException { try { - return mSipService.createSession(localProfile, listener); + ISipSession s = mSipService.createSession(localProfile, null); + return new SipSession(s, listener); } catch (RemoteException e) { throw new SipException("createSipSession()", e); } diff --git a/voip/java/android/net/sip/SipProfile.java b/voip/java/android/net/sip/SipProfile.java index 88bfba9..6d5cb3c 100644 --- a/voip/java/android/net/sip/SipProfile.java +++ b/voip/java/android/net/sip/SipProfile.java @@ -48,7 +48,6 @@ public class SipProfile implements Parcelable, Serializable, Cloneable { private boolean mAutoRegistration = true; private transient int mCallingUid = 0; - /** @hide */ public static final Parcelable.Creator<SipProfile> CREATOR = new Parcelable.Creator<SipProfile>() { public SipProfile createFromParcel(Parcel in) { @@ -287,7 +286,7 @@ public class SipProfile implements Parcelable, Serializable, Cloneable { mCallingUid = in.readInt(); } - /** @hide */ + @Override public void writeToParcel(Parcel out, int flags) { out.writeSerializable(mAddress); out.writeString(mProxyAddress); @@ -300,7 +299,7 @@ public class SipProfile implements Parcelable, Serializable, Cloneable { out.writeInt(mCallingUid); } - /** @hide */ + @Override public int describeContents() { return 0; } diff --git a/voip/java/android/net/sip/SipSession.java b/voip/java/android/net/sip/SipSession.java new file mode 100644 index 0000000..0cc7206 --- /dev/null +++ b/voip/java/android/net/sip/SipSession.java @@ -0,0 +1,531 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.net.sip; + +import android.os.RemoteException; +import android.util.Log; + +/** + * A SIP session that is associated with a SIP dialog or a standalone + * transaction not within a dialog. + * @hide + */ +public final class SipSession { + private static final String TAG = "SipSession"; + + /** + * Defines {@link SipSession} states. + * @hide + */ + public static class State { + /** When session is ready to initiate a call or transaction. */ + public static final int READY_TO_CALL = 0; + + /** When the registration request is sent out. */ + public static final int REGISTERING = 1; + + /** When the unregistration request is sent out. */ + public static final int DEREGISTERING = 2; + + /** When an INVITE request is received. */ + public static final int INCOMING_CALL = 3; + + /** When an OK response is sent for the INVITE request received. */ + public static final int INCOMING_CALL_ANSWERING = 4; + + /** When an INVITE request is sent. */ + public static final int OUTGOING_CALL = 5; + + /** When a RINGING response is received for the INVITE request sent. */ + public static final int OUTGOING_CALL_RING_BACK = 6; + + /** When a CANCEL request is sent for the INVITE request sent. */ + public static final int OUTGOING_CALL_CANCELING = 7; + + /** When a call is established. */ + public static final int IN_CALL = 8; + + /** When an OPTIONS request is sent. */ + public static final int PINGING = 9; + + /** Not defined. */ + public static final int NOT_DEFINED = 101; + + /** + * Converts the state to string. + */ + public static String toString(int state) { + switch (state) { + case READY_TO_CALL: + return "READY_TO_CALL"; + case REGISTERING: + return "REGISTERING"; + case DEREGISTERING: + return "DEREGISTERING"; + case INCOMING_CALL: + return "INCOMING_CALL"; + case INCOMING_CALL_ANSWERING: + return "INCOMING_CALL_ANSWERING"; + case OUTGOING_CALL: + return "OUTGOING_CALL"; + case OUTGOING_CALL_RING_BACK: + return "OUTGOING_CALL_RING_BACK"; + case OUTGOING_CALL_CANCELING: + return "OUTGOING_CALL_CANCELING"; + case IN_CALL: + return "IN_CALL"; + case PINGING: + return "PINGING"; + default: + return "NOT_DEFINED"; + } + } + + private State() { + } + } + + /** + * Listener class that listens to {@link SipSession} events. + * @hide + */ + public static class Listener { + /** + * Called when an INVITE request is sent to initiate a new call. + * + * @param session the session object that carries out the transaction + */ + public void onCalling(SipSession session) { + } + + /** + * Called when an INVITE request is received. + * + * @param session the session object that carries out the transaction + * @param caller the SIP profile of the caller + * @param sessionDescription the caller's session description + */ + public void onRinging(SipSession session, SipProfile caller, + String sessionDescription) { + } + + /** + * Called when a RINGING response is received for the INVITE request sent + * + * @param session the session object that carries out the transaction + */ + public void onRingingBack(SipSession session) { + } + + /** + * Called when the session is established. + * + * @param session the session object that is associated with the dialog + * @param sessionDescription the peer's session description + */ + public void onCallEstablished(SipSession session, + String sessionDescription) { + } + + /** + * Called when the session is terminated. + * + * @param session the session object that is associated with the dialog + */ + public void onCallEnded(SipSession session) { + } + + /** + * Called when the peer is busy during session initialization. + * + * @param session the session object that carries out the transaction + */ + public void onCallBusy(SipSession session) { + } + + /** + * Called when an error occurs during session initialization and + * termination. + * + * @param session the session object that carries out the transaction + * @param errorCode error code defined in {@link SipErrorCode} + * @param errorMessage error message + */ + public void onError(SipSession session, int errorCode, + String errorMessage) { + } + + /** + * Called when an error occurs during session modification negotiation. + * + * @param session the session object that carries out the transaction + * @param errorCode error code defined in {@link SipErrorCode} + * @param errorMessage error message + */ + public void onCallChangeFailed(SipSession session, int errorCode, + String errorMessage) { + } + + /** + * Called when a registration request is sent. + * + * @param session the session object that carries out the transaction + */ + public void onRegistering(SipSession session) { + } + + /** + * Called when registration is successfully done. + * + * @param session the session object that carries out the transaction + * @param duration duration in second before the registration expires + */ + public void onRegistrationDone(SipSession session, int duration) { + } + + /** + * Called when the registration fails. + * + * @param session the session object that carries out the transaction + * @param errorCode error code defined in {@link SipErrorCode} + * @param errorMessage error message + */ + public void onRegistrationFailed(SipSession session, int errorCode, + String errorMessage) { + } + + /** + * Called when the registration gets timed out. + * + * @param session the session object that carries out the transaction + */ + public void onRegistrationTimeout(SipSession session) { + } + } + + private final ISipSession mSession; + private Listener mListener; + + SipSession(ISipSession realSession) { + mSession = realSession; + if (realSession != null) { + try { + realSession.setListener(createListener()); + } catch (RemoteException e) { + Log.e(TAG, "SipSession.setListener(): " + e); + } + } + } + + SipSession(ISipSession realSession, Listener listener) { + this(realSession); + setListener(listener); + } + + /** + * Gets the IP address of the local host on which this SIP session runs. + * + * @return the IP address of the local host + */ + public String getLocalIp() { + try { + return mSession.getLocalIp(); + } catch (RemoteException e) { + Log.e(TAG, "getLocalIp(): " + e); + return "127.0.0.1"; + } + } + + /** + * Gets the SIP profile that this session is associated with. + * + * @return the SIP profile that this session is associated with + */ + public SipProfile getLocalProfile() { + try { + return mSession.getLocalProfile(); + } catch (RemoteException e) { + Log.e(TAG, "getLocalProfile(): " + e); + return null; + } + } + + /** + * Gets the SIP profile that this session is connected to. Only available + * when the session is associated with a SIP dialog. + * + * @return the SIP profile that this session is connected to + */ + public SipProfile getPeerProfile() { + try { + return mSession.getPeerProfile(); + } catch (RemoteException e) { + Log.e(TAG, "getPeerProfile(): " + e); + return null; + } + } + + /** + * Gets the session state. The value returned must be one of the states in + * {@link SipSessionState}. + * + * @return the session state + */ + public int getState() { + try { + return mSession.getState(); + } catch (RemoteException e) { + Log.e(TAG, "getState(): " + e); + return State.NOT_DEFINED; + } + } + + /** + * Checks if the session is in a call. + * + * @return true if the session is in a call + */ + public boolean isInCall() { + try { + return mSession.isInCall(); + } catch (RemoteException e) { + Log.e(TAG, "isInCall(): " + e); + return false; + } + } + + /** + * Gets the call ID of the session. + * + * @return the call ID + */ + public String getCallId() { + try { + return mSession.getCallId(); + } catch (RemoteException e) { + Log.e(TAG, "getCallId(): " + e); + return null; + } + } + + + /** + * Sets the listener to listen to the session events. A {@code SipSession} + * can only hold one listener at a time. Subsequent calls to this method + * override the previous listener. + * + * @param listener to listen to the session events of this object + */ + public void setListener(Listener listener) { + mListener = listener; + } + + + /** + * Performs registration to the server specified by the associated local + * profile. The session listener is called back upon success or failure of + * registration. The method is only valid to call when the session state is + * in {@link SipSessionState#READY_TO_CALL}. + * + * @param duration duration in second before the registration expires + * @see Listener + */ + public void register(int duration) { + try { + mSession.register(duration); + } catch (RemoteException e) { + Log.e(TAG, "register(): " + e); + } + } + + /** + * Performs unregistration to the server specified by the associated local + * profile. Unregistration is technically the same as registration with zero + * expiration duration. The session listener is called back upon success or + * failure of unregistration. The method is only valid to call when the + * session state is in {@link SipSessionState#READY_TO_CALL}. + * + * @see Listener + */ + public void unregister() { + try { + mSession.unregister(); + } catch (RemoteException e) { + Log.e(TAG, "unregister(): " + e); + } + } + + /** + * Initiates a call to the specified profile. The session listener is called + * back upon defined session events. The method is only valid to call when + * the session state is in {@link SipSessionState#READY_TO_CALL}. + * + * @param callee the SIP profile to make the call to + * @param sessionDescription the session description of this call + * @param timeout the session will be timed out if the call is not + * established within {@code timeout} seconds. Default value (defined + * by SIP protocol) is used if {@code timeout} is zero or negative. + * @see Listener + */ + public void makeCall(SipProfile callee, String sessionDescription, + int timeout) { + try { + mSession.makeCall(callee, sessionDescription, timeout); + } catch (RemoteException e) { + Log.e(TAG, "makeCall(): " + e); + } + } + + /** + * Answers an incoming call with the specified session description. The + * method is only valid to call when the session state is in + * {@link SipSessionState#INCOMING_CALL}. + * + * @param sessionDescription the session description to answer this call + * @param timeout the session will be timed out if the call is not + * established within {@code timeout} seconds. Default value (defined + * by SIP protocol) is used if {@code timeout} is zero or negative. + */ + public void answerCall(String sessionDescription, int timeout) { + try { + mSession.answerCall(sessionDescription, timeout); + } catch (RemoteException e) { + Log.e(TAG, "answerCall(): " + e); + } + } + + /** + * Ends an established call, terminates an outgoing call or rejects an + * incoming call. The method is only valid to call when the session state is + * in {@link SipSessionState#IN_CALL}, + * {@link SipSessionState#INCOMING_CALL}, + * {@link SipSessionState#OUTGOING_CALL} or + * {@link SipSessionState#OUTGOING_CALL_RING_BACK}. + */ + public void endCall() { + try { + mSession.endCall(); + } catch (RemoteException e) { + Log.e(TAG, "endCall(): " + e); + } + } + + /** + * Changes the session description during a call. The method is only valid + * to call when the session state is in {@link SipSessionState#IN_CALL}. + * + * @param sessionDescription the new session description + * @param timeout the session will be timed out if the call is not + * established within {@code timeout} seconds. Default value (defined + * by SIP protocol) is used if {@code timeout} is zero or negative. + */ + public void changeCall(String sessionDescription, int timeout) { + try { + mSession.changeCall(sessionDescription, timeout); + } catch (RemoteException e) { + Log.e(TAG, "changeCall(): " + e); + } + } + + ISipSession getRealSession() { + return mSession; + } + + private ISipSessionListener createListener() { + return new ISipSessionListener.Stub() { + public void onCalling(ISipSession session) { + if (mListener != null) { + mListener.onCalling(SipSession.this); + } + } + + public void onRinging(ISipSession session, SipProfile caller, + String sessionDescription) { + if (mListener != null) { + mListener.onRinging(SipSession.this, caller, + sessionDescription); + } + } + + public void onRingingBack(ISipSession session) { + if (mListener != null) { + mListener.onRingingBack(SipSession.this); + } + } + + public void onCallEstablished(ISipSession session, + String sessionDescription) { + if (mListener != null) { + mListener.onCallEstablished(SipSession.this, + sessionDescription); + } + } + + public void onCallEnded(ISipSession session) { + if (mListener != null) { + mListener.onCallEnded(SipSession.this); + } + } + + public void onCallBusy(ISipSession session) { + if (mListener != null) { + mListener.onCallBusy(SipSession.this); + } + } + + public void onCallChangeFailed(ISipSession session, int errorCode, + String message) { + if (mListener != null) { + mListener.onCallChangeFailed(SipSession.this, errorCode, + message); + } + } + + public void onError(ISipSession session, int errorCode, String message) { + if (mListener != null) { + mListener.onError(SipSession.this, errorCode, message); + } + } + + public void onRegistering(ISipSession session) { + if (mListener != null) { + mListener.onRegistering(SipSession.this); + } + } + + public void onRegistrationDone(ISipSession session, int duration) { + if (mListener != null) { + mListener.onRegistrationDone(SipSession.this, duration); + } + } + + public void onRegistrationFailed(ISipSession session, int errorCode, + String message) { + if (mListener != null) { + mListener.onRegistrationFailed(SipSession.this, errorCode, + message); + } + } + + public void onRegistrationTimeout(ISipSession session) { + if (mListener != null) { + mListener.onRegistrationTimeout(SipSession.this); + } + } + }; + } +} diff --git a/voip/java/android/net/sip/SipSessionState.java b/voip/java/android/net/sip/SipSessionState.java deleted file mode 100644 index 31e9d3f..0000000 --- a/voip/java/android/net/sip/SipSessionState.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package android.net.sip; - -/** - * Defines {@link ISipSession} states. - * @hide - */ -public class SipSessionState { - /** When session is ready to initiate a call or transaction. */ - public static final int READY_TO_CALL = 0; - - /** When the registration request is sent out. */ - public static final int REGISTERING = 1; - - /** When the unregistration request is sent out. */ - public static final int DEREGISTERING = 2; - - /** When an INVITE request is received. */ - public static final int INCOMING_CALL = 3; - - /** When an OK response is sent for the INVITE request received. */ - public static final int INCOMING_CALL_ANSWERING = 4; - - /** When an INVITE request is sent. */ - public static final int OUTGOING_CALL = 5; - - /** When a RINGING response is received for the INVITE request sent. */ - public static final int OUTGOING_CALL_RING_BACK = 6; - - /** When a CANCEL request is sent for the INVITE request sent. */ - public static final int OUTGOING_CALL_CANCELING = 7; - - /** When a call is established. */ - public static final int IN_CALL = 8; - - /** Some error occurs when making a remote call to {@link ISipSession}. */ - public static final int REMOTE_ERROR = 9; - - /** When an OPTIONS request is sent. */ - public static final int PINGING = 10; - - /** Not defined. */ - public static final int NOT_DEFINED = 101; - - /** - * Converts the state to string. - */ - public static String toString(int state) { - switch (state) { - case READY_TO_CALL: - return "READY_TO_CALL"; - case REGISTERING: - return "REGISTERING"; - case DEREGISTERING: - return "DEREGISTERING"; - case INCOMING_CALL: - return "INCOMING_CALL"; - case INCOMING_CALL_ANSWERING: - return "INCOMING_CALL_ANSWERING"; - case OUTGOING_CALL: - return "OUTGOING_CALL"; - case OUTGOING_CALL_RING_BACK: - return "OUTGOING_CALL_RING_BACK"; - case OUTGOING_CALL_CANCELING: - return "OUTGOING_CALL_CANCELING"; - case IN_CALL: - return "IN_CALL"; - case REMOTE_ERROR: - return "REMOTE_ERROR"; - case PINGING: - return "PINGING"; - default: - return "NOT_DEFINED"; - } - } - - private SipSessionState() { - } -} |