diff options
80 files changed, 3987 insertions, 2467 deletions
diff --git a/api/current.xml b/api/current.xml index 5e57d80c2676..80428bd9e6ad 100644 --- a/api/current.xml +++ b/api/current.xml @@ -21741,6 +21741,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" diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java index f901bfbda10f..4dee350e9d77 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,18 @@ 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.DataInputStream; import java.io.File; +import java.io.FileInputStream; 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 +106,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 +481,210 @@ public class Am { } } + class MyActivityController extends IActivityController.Stub { + 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; + + @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(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(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(STATE_ANR); + if (result == RESULT_ANR_KILL) return -1; + if (result == RESULT_ANR_WAIT) return 1; + return 0; + } + } + + int waitControllerLocked(int state) { + mState = state; + System.out.println(""); + printMessageForState(); + + while (mState != STATE_NORMAL) { + try { + wait(); + } catch (InterruptedException e) { + } + } + + 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 { + MyActivityController controller = new MyActivityController(); + controller.run(); + } + private class IntentReceiver extends IIntentReceiver.Stub { private boolean mFinished = false; @@ -649,6 +862,8 @@ 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\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/stagefright/stagefright.cpp b/cmds/stagefright/stagefright.cpp index 8ab94adb6204..eb4b733a47da 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> @@ -30,7 +34,6 @@ #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 +46,8 @@ #include <media/stagefright/foundation/hexdump.h> #include <media/stagefright/MPEG4Writer.h> +#include <fcntl.h> + using namespace android; static long gNumRepetitions; @@ -120,7 +125,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 +211,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 +272,98 @@ 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: + sp<MediaSource> mSource; + bool mIsAVC; + + DISALLOW_EVIL_CONSTRUCTORS(DetectSyncSource); +}; + +DetectSyncSource::DetectSyncSource(const sp<MediaSource> &source) + : mSource(source), + mIsAVC(false) { + const char *mime; + CHECK(mSource->getFormat()->findCString(kKeyMIMEType, &mime)); + + mIsAVC = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC); +} + +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 (mIsAVC && isIDRFrame(*buffer)) { + (*buffer)->meta_data()->setInt32(kKeyIsSyncFrame, true); + } else { + (*buffer)->meta_data()->setInt32(kKeyIsSyncFrame, false); + } + + return OK; +} + +//////////////////////////////////////////////////////////////////////////////// + +static void writeSourceToMP4( + sp<MediaSource> &source, bool syncInfoPresent) { + if (!syncInfoPresent) { + source = new DetectSyncSource(source); + } + sp<MPEG4Writer> writer = new MPEG4Writer(gWriteMP4Filename.string()); - CHECK_EQ(writer->addSource(source), OK); + // at most one minute. + writer->setMaxFileDuration(60000000ll); + + CHECK_EQ(writer->addSource(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 +372,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 +424,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 +570,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 +619,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,6 +668,8 @@ 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); @@ -625,6 +716,8 @@ int main(int argc, char **argv) { } extractor = rtspController.get(); + + syncInfoPresent = false; } else { extractor = MediaExtractor::Create(dataSource); if (extractor == NULL) { @@ -674,7 +767,7 @@ int main(int argc, char **argv) { } if (gWriteMP4) { - writeSourceToMP4(mediaSource); + writeSourceToMP4(mediaSource, syncInfoPresent); } else if (seekTest) { performSeekTest(mediaSource); } else { diff --git a/core/java/android/animation/ValueAnimator.java b/core/java/android/animation/ValueAnimator.java index 1e2bbccb7447..02b2dcece313 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 3f7490454345..2ff88da3f9d9 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 c76a517c14ba..aca8305c8245 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 3df1790ffcfc..4cb70262f7c9 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/os/BatteryStats.java b/core/java/android/os/BatteryStats.java index ba8014f2a3fa..d49c8be8bb36 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 f695dbb4cdd4..1235b4d97c0c 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/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index 4e61ddf414c7..66149ac4ef3b 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 000000000000..d53860cd2d79 --- /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 000000000000..f4ee7ee025e6 --- /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/res/res/drawable-xlarge/unlock_default.png b/core/res/res/drawable-xlarge/unlock_default.png Binary files differnew file mode 100644 index 000000000000..0a441c094a45 --- /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 000000000000..09b05262db0c --- /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 000000000000..1ac6d547f390 --- /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 000000000000..21bfa24ddbce --- /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 47618009113c..b3645aada1f2 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 bb398f6a112b..6c99ccac7729 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/data/fonts/Ahem.ttf b/data/fonts/Ahem.ttf Binary files differdeleted file mode 100644 index 17e6c60484a6..000000000000 --- a/data/fonts/Ahem.ttf +++ /dev/null diff --git a/data/fonts/fonts.xml b/data/fonts/fonts.xml index 6bcaaaf9059a..1fd7bba2ef3c 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/graphics/java/android/renderscript/Program.java b/graphics/java/android/renderscript/Program.java index c6ed72a53d84..ffcdbbc5a818 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 00c5cf1f18b9..c1d642866374 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 119db69598e1..63e2598c2b9d 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 159e070681e6..2aa3e84fb654 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 ee2080ef7319..6aed11b730c5 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/libs/rs/java/Samples/res/raw/multitexf.glsl b/libs/rs/java/Samples/res/raw/multitexf.glsl new file mode 100644 index 000000000000..91151ada6076 --- /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/RsRenderStatesRS.java b/libs/rs/java/Samples/src/com/android/samples/RsRenderStatesRS.java index 18b94d9f5af4..d4e83d3f68f1 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 c7bea93c9d44..659e1e42e375 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/rs.spec b/libs/rs/rs.spec index 21cbc5033232..2b7928fd265e 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 3dbdbfb511b2..3681bc29bacf 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 e38ba5517be7..bce9c13fbd3a 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 c437606cdb48..f30915ea6ae5 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 d0909c824df0..0b9e28c658a0 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 ae6a6ccb2ea7..50bca8578a8d 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 5709f2a8e79d..893598f06b11 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 0f815a2fa199..bd5713e9e78e 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 24414912fbc8..9c664627a2bc 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 e7329c250d5e..a8f34c3ea26e 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 8f5c65379412..d511d213f301 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 fb78b3f47b80..1cf9ca7ed69a 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 6446b55dd663..c3ef3562fd53 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 65ce541cfdc8..355df2b9612a 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 2f8f79a9781e..4be50593db4f 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 79cfd412c145..478cc95b9e07 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 5b51e20616c7..33faa878849a 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/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java index f72eaa41b9b8..bc5f9fa2490d 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 b64299a13f84..403ed5870246 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 1842cb2aa980..227ee92f6990 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/mtp/MtpCursor.cpp b/media/mtp/MtpCursor.cpp index 8c964b489bfd..865a2949d5a8 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 3f84753f5ad9..9e9833f8b430 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 6332b4ef2423..84a3e2ced84f 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 5899bc1c9309..9f2f98e388cf 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 d99fc1e2474c..bc944a038455 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 a12edf238e7d..fee4609378b0 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 d71ff7620944..ee29f12faf73 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/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java index d98bd7dca5cf..0ca057233766 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 f8c0aba4b448..3583ab9f261e 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 bd774cedca7c..c0475224282f 100755 --- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java +++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java @@ -290,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... @@ -363,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. @@ -374,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; } } @@ -2120,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 @@ -2153,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 56de765554d8..8a732ed19a4c 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/ProcessStats.java b/services/java/com/android/server/ProcessStats.java index 5bdadcc865b8..43dbcc012cf3 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/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index 6b5b3bdf14a4..8e22652ae5db 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; @@ -780,9 +793,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 @@ -1195,12 +1213,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 +1411,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 +1532,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 +1548,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 +1585,7 @@ public final class ActivityManagerService extends ActivityManagerNative updateCpuStatsNow(); synchronized (this) { synchronized(mPidsSelfLocked) { - mOnBattery = onBattery; + mOnBattery = DEBUG_POWER ? true : onBattery; } } } @@ -2795,6 +2815,16 @@ public final class ActivityManagerService extends ActivityManagerNative 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 +2875,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 +2890,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(); @@ -3742,7 +3772,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 +5685,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 +5738,6 @@ public final class ActivityManagerService extends ActivityManagerNative mWindowManager.setEventDispatching(true); mSleeping = false; mMainStack.resumeTopActivityLocked(null); - mHandler.removeMessages(CHECK_EXCESSIVE_WAKE_LOCKS_MSG); } } @@ -7055,12 +7084,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 +7122,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 +7248,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 +7278,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 +7310,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 +7318,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 +7326,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 +7334,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 +7401,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 +7415,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 +7927,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 +8694,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 +8736,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 +8769,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 +8804,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 +9056,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 +9107,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 +9563,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 +9784,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); @@ -11326,6 +11473,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 +11481,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 +11611,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 +11647,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 +11683,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 +11725,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 +11747,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 +11765,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 +11905,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 +12011,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 +12027,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 67df707341ed..404c6be917e7 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 f35a68e734d4..e5aceb45c4d9 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 f1dcd5a6dd25..3f43e1c7601a 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 b4c224160545..66a2c05e1081 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; @@ -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 a4cd1029d8e0..f8be0a82b94d 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 129be4e433e2..ff887e436d97 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 22ff10ca5a3c..5a9e9ebb1934 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 06c86ddef66e..b353bffe684c 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 9fcf12dbbfea..4791fbdc7244 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; @@ -91,7 +91,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 @@ -710,8 +710,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, + SESSION_TIMEOUT); mSipAudioCall.setRingbackToneEnabled(false); mSipAudioCall.setListener(mAdapter); } @@ -807,20 +807,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 48c452027b0f..5fb09a70129a 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/voip/java/android/net/sip/SdpSessionDescription.java b/voip/java/android/net/sip/SdpSessionDescription.java deleted file mode 100644 index f6ae83791e52..000000000000 --- 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 a120d166799e..000000000000 --- 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 d476f0b784fe..000000000000 --- 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 0069fe065284..2135fcb59232 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 5eecc0555e8f..000000000000 --- 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 31768d731a2d..5976a0440c2b 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 88bfba936b2f..6d5cb3cf9c92 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 000000000000..0cc7206ab813 --- /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 31e9d3f5992c..000000000000 --- 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() { - } -} |