summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--cmds/installd/commands.c56
-rw-r--r--cmds/installd/installd.c63
-rw-r--r--cmds/installd/installd.h12
-rw-r--r--cmds/installd/utils.c72
-rw-r--r--cmds/pm/src/com/android/commands/pm/Pm.java72
-rw-r--r--core/java/android/app/ApplicationPackageManager.java13
-rw-r--r--core/java/android/content/pm/IPackageManager.aidl4
-rw-r--r--core/java/android/content/pm/PackageManager.java46
-rw-r--r--core/java/android/content/pm/PackageParser.java1
-rw-r--r--core/java/android/content/pm/UserInfo.aidl20
-rw-r--r--core/java/android/content/pm/UserInfo.java3
-rw-r--r--core/java/android/speech/tts/FileSynthesisRequest.java17
-rw-r--r--core/java/android/speech/tts/PlaybackSynthesisRequest.java92
-rw-r--r--core/java/android/speech/tts/SynthesisRequest.java13
-rw-r--r--core/java/android/speech/tts/TextToSpeechService.java11
-rw-r--r--core/java/android/widget/ListView.java22
-rw-r--r--core/jni/android/graphics/Canvas.cpp39
-rw-r--r--core/jni/android/graphics/RtlProperties.h2
-rw-r--r--core/jni/android/graphics/TextLayoutCache.cpp26
-rw-r--r--include/gui/SurfaceTexture.h25
-rw-r--r--include/gui/SurfaceTextureClient.h22
-rw-r--r--libs/gui/SurfaceTexture.cpp72
-rw-r--r--libs/gui/SurfaceTextureClient.cpp65
-rw-r--r--media/libstagefright/MPEG4Extractor.cpp91
-rw-r--r--media/libstagefright/NuCachedSource2.cpp24
-rw-r--r--media/libstagefright/SampleTable.cpp159
-rw-r--r--media/libstagefright/include/MPEG4Extractor.h2
-rw-r--r--media/libstagefright/include/NuCachedSource2.h4
-rw-r--r--media/libstagefright/include/SampleTable.h12
-rw-r--r--opengl/libs/GLES2_dbg/Android.mk2
-rw-r--r--opengl/libs/GLES2_dbg/src/dbgcontext.cpp37
-rw-r--r--opengl/libs/GLES2_dbg/src/header.h5
-rw-r--r--opengl/libs/GLES2_dbg/src/server.cpp5
-rw-r--r--opengl/libs/GLES2_dbg/src/vertex.cpp16
-rw-r--r--opengl/libs/GLES2_dbg/test/Android.mk39
-rw-r--r--opengl/libs/GLES2_dbg/test/test_main.cpp234
-rw-r--r--opengl/libs/GLES2_dbg/test/test_server.cpp197
-rw-r--r--opengl/libs/GLES2_dbg/test/test_socket.cpp474
-rw-r--r--services/java/com/android/server/pm/Installer.java26
-rw-r--r--services/java/com/android/server/pm/PackageManagerService.java68
-rw-r--r--services/java/com/android/server/pm/UserManager.java (renamed from services/java/com/android/server/pm/UserDetails.java)136
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java (renamed from services/tests/servicestests/src/com/android/server/pm/UserDetailsTest.java)31
-rw-r--r--wifi/java/android/net/wifi/WifiStateMachine.java22
43 files changed, 2052 insertions, 300 deletions
diff --git a/cmds/installd/commands.c b/cmds/installd/commands.c
index 80ba1e9..9aa70a4 100644
--- a/cmds/installd/commands.c
+++ b/cmds/installd/commands.c
@@ -48,6 +48,11 @@ int install(const char *pkgname, uid_t uid, gid_t gid)
LOGE("cannot create dir '%s': %s\n", pkgdir, strerror(errno));
return -errno;
}
+ if (chmod(pkgdir, 0751) < 0) {
+ LOGE("cannot chmod dir '%s': %s\n", pkgdir, strerror(errno));
+ unlink(pkgdir);
+ return -errno;
+ }
if (chown(pkgdir, uid, gid) < 0) {
LOGE("cannot chown dir '%s': %s\n", pkgdir, strerror(errno));
unlink(pkgdir);
@@ -58,6 +63,12 @@ int install(const char *pkgname, uid_t uid, gid_t gid)
unlink(pkgdir);
return -errno;
}
+ if (chmod(libdir, 0755) < 0) {
+ LOGE("cannot chmod dir '%s': %s\n", libdir, strerror(errno));
+ unlink(libdir);
+ unlink(pkgdir);
+ return -errno;
+ }
if (chown(libdir, AID_SYSTEM, AID_SYSTEM) < 0) {
LOGE("cannot chown dir '%s': %s\n", libdir, strerror(errno));
unlink(libdir);
@@ -67,15 +78,15 @@ int install(const char *pkgname, uid_t uid, gid_t gid)
return 0;
}
-int uninstall(const char *pkgname)
+int uninstall(const char *pkgname, uid_t persona)
{
char pkgdir[PKG_PATH_MAX];
- if (create_pkg_path(pkgdir, pkgname, PKG_DIR_POSTFIX, 0))
+ if (create_pkg_path(pkgdir, pkgname, PKG_DIR_POSTFIX, persona))
return -1;
- /* delete contents AND directory, no exceptions */
- return delete_dir_contents(pkgdir, 1, 0);
+ /* delete contents AND directory, no exceptions */
+ return delete_dir_contents(pkgdir, 1, NULL);
}
int renamepkg(const char *oldpkgname, const char *newpkgname)
@@ -95,17 +106,48 @@ int renamepkg(const char *oldpkgname, const char *newpkgname)
return 0;
}
-int delete_user_data(const char *pkgname)
+int delete_user_data(const char *pkgname, uid_t persona)
{
char pkgdir[PKG_PATH_MAX];
- if (create_pkg_path(pkgdir, pkgname, PKG_DIR_POSTFIX, 0))
+ if (create_pkg_path(pkgdir, pkgname, PKG_DIR_POSTFIX, persona))
return -1;
- /* delete contents, excluding "lib", but not the directory itself */
+ /* delete contents, excluding "lib", but not the directory itself */
return delete_dir_contents(pkgdir, 0, "lib");
}
+int make_user_data(const char *pkgname, uid_t uid, uid_t persona)
+{
+ char pkgdir[PKG_PATH_MAX];
+ char real_libdir[PKG_PATH_MAX];
+
+ // Create the data dir for the package
+ if (create_pkg_path(pkgdir, pkgname, PKG_DIR_POSTFIX, persona)) {
+ return -1;
+ }
+ if (mkdir(pkgdir, 0751) < 0) {
+ LOGE("cannot create dir '%s': %s\n", pkgdir, strerror(errno));
+ return -errno;
+ }
+ if (chown(pkgdir, uid, uid) < 0) {
+ LOGE("cannot chown dir '%s': %s\n", pkgdir, strerror(errno));
+ unlink(pkgdir);
+ return -errno;
+ }
+ return 0;
+}
+
+int delete_persona(uid_t persona)
+{
+ char pkgdir[PKG_PATH_MAX];
+
+ if (create_persona_path(pkgdir, persona))
+ return -1;
+
+ return delete_dir_contents(pkgdir, 1, NULL);
+}
+
int delete_cache(const char *pkgname)
{
char cachedir[PKG_PATH_MAX];
diff --git a/cmds/installd/installd.c b/cmds/installd/installd.c
index e0d0f97..c062d36 100644
--- a/cmds/installd/installd.c
+++ b/cmds/installd/installd.c
@@ -49,7 +49,7 @@ static int do_rm_dex(char **arg, char reply[REPLY_MAX])
static int do_remove(char **arg, char reply[REPLY_MAX])
{
- return uninstall(arg[0]); /* pkgname */
+ return uninstall(arg[0], atoi(arg[1])); /* pkgname, userid */
}
static int do_rename(char **arg, char reply[REPLY_MAX])
@@ -92,7 +92,17 @@ static int do_get_size(char **arg, char reply[REPLY_MAX])
static int do_rm_user_data(char **arg, char reply[REPLY_MAX])
{
- return delete_user_data(arg[0]); /* pkgname */
+ return delete_user_data(arg[0], atoi(arg[1])); /* pkgname, userid */
+}
+
+static int do_mk_user_data(char **arg, char reply[REPLY_MAX])
+{
+ return make_user_data(arg[0], atoi(arg[1]), atoi(arg[2])); /* pkgname, uid, userid */
+}
+
+static int do_rm_user(char **arg, char reply[REPLY_MAX])
+{
+ return delete_persona(atoi(arg[0])); /* userid */
}
static int do_movefiles(char **arg, char reply[REPLY_MAX])
@@ -122,16 +132,18 @@ struct cmdinfo cmds[] = {
{ "dexopt", 3, do_dexopt },
{ "movedex", 2, do_move_dex },
{ "rmdex", 1, do_rm_dex },
- { "remove", 1, do_remove },
+ { "remove", 2, do_remove },
{ "rename", 2, do_rename },
{ "freecache", 1, do_free_cache },
{ "rmcache", 1, do_rm_cache },
{ "protect", 2, do_protect },
{ "getsize", 3, do_get_size },
- { "rmuserdata", 1, do_rm_user_data },
+ { "rmuserdata", 2, do_rm_user_data },
{ "movefiles", 0, do_movefiles },
{ "linklib", 2, do_linklib },
{ "unlinklib", 1, do_unlinklib },
+ { "mkuserdata", 3, do_mk_user_data },
+ { "rmuser", 1, do_rm_user },
};
static int readx(int s, void *_buf, int count)
@@ -286,14 +298,50 @@ int initialize_globals() {
return -1;
}
+ // append "app/" to dirs[0]
+ char *system_app_path = build_string2(android_system_dirs.dirs[0].path, APP_SUBDIR);
+ android_system_dirs.dirs[0].path = system_app_path;
+ android_system_dirs.dirs[0].len = strlen(system_app_path);
+
// vendor
// TODO replace this with an environment variable (doesn't exist yet)
- android_system_dirs.dirs[1].path = "/vendor/";
+ android_system_dirs.dirs[1].path = "/vendor/app/";
android_system_dirs.dirs[1].len = strlen(android_system_dirs.dirs[1].path);
return 0;
}
+int initialize_directories() {
+ // /data/user
+ char *user_data_dir = build_string2(android_data_dir.path, SECONDARY_USER_PREFIX);
+ // /data/data
+ char *legacy_data_dir = build_string2(android_data_dir.path, PRIMARY_USER_PREFIX);
+ // /data/user/0
+ char *primary_data_dir = build_string3(android_data_dir.path, SECONDARY_USER_PREFIX,
+ "0");
+ int ret = -1;
+ if (user_data_dir != NULL && primary_data_dir != NULL && legacy_data_dir != NULL) {
+ ret = 0;
+ // Make the /data/user directory if necessary
+ if (access(user_data_dir, R_OK) < 0) {
+ if (mkdir(user_data_dir, 0755) < 0) {
+ return -1;
+ }
+ if (chown(user_data_dir, AID_SYSTEM, AID_SYSTEM) < 0) {
+ return -1;
+ }
+ }
+ // Make the /data/user/0 symlink to /data/data if necessary
+ if (access(primary_data_dir, R_OK) < 0) {
+ ret = symlink(legacy_data_dir, primary_data_dir);
+ }
+ free(user_data_dir);
+ free(legacy_data_dir);
+ free(primary_data_dir);
+ }
+ return ret;
+}
+
int main(const int argc, const char *argv[]) {
char buf[BUFFER_MAX];
struct sockaddr addr;
@@ -305,6 +353,11 @@ int main(const int argc, const char *argv[]) {
exit(1);
}
+ if (initialize_directories() < 0) {
+ LOGE("Could not create directories; exiting.\n");
+ exit(1);
+ }
+
lsocket = android_get_control_socket(SOCKET_PATH);
if (lsocket < 0) {
LOGE("Failed to get socket from environment: %s\n", strerror(errno));
diff --git a/cmds/installd/installd.h b/cmds/installd/installd.h
index cbca135..e5f6739 100644
--- a/cmds/installd/installd.h
+++ b/cmds/installd/installd.h
@@ -102,6 +102,9 @@ int create_pkg_path(char path[PKG_PATH_MAX],
const char *postfix,
uid_t persona);
+int create_persona_path(char path[PKG_PATH_MAX],
+ uid_t persona);
+
int is_valid_package_name(const char* pkgname);
int create_cache_path(char path[PKG_PATH_MAX], const char *src);
@@ -124,12 +127,17 @@ int validate_apk_path(const char *path);
int append_and_increment(char** dst, const char* src, size_t* dst_size);
+char *build_string2(char *s1, char *s2);
+char *build_string3(char *s1, char *s2, char *s3);
+
/* commands.c */
int install(const char *pkgname, uid_t uid, gid_t gid);
-int uninstall(const char *pkgname);
+int uninstall(const char *pkgname, uid_t persona);
int renamepkg(const char *oldpkgname, const char *newpkgname);
-int delete_user_data(const char *pkgname);
+int delete_user_data(const char *pkgname, uid_t persona);
+int make_user_data(const char *pkgname, uid_t uid, uid_t persona);
+int delete_persona(uid_t persona);
int delete_cache(const char *pkgname);
int move_dex(const char *src, const char *dst);
int rm_dex(const char *path);
diff --git a/cmds/installd/utils.c b/cmds/installd/utils.c
index f37a6fb..3099b83 100644
--- a/cmds/installd/utils.c
+++ b/cmds/installd/utils.c
@@ -96,6 +96,46 @@ int create_pkg_path(char path[PKG_PATH_MAX],
}
/**
+ * Create the path name for user data for a certain persona.
+ * Returns 0 on success, and -1 on failure.
+ */
+int create_persona_path(char path[PKG_PATH_MAX],
+ uid_t persona)
+{
+ size_t uid_len;
+ char* persona_prefix;
+ if (persona == 0) {
+ persona_prefix = PRIMARY_USER_PREFIX;
+ uid_len = 0;
+ } else {
+ persona_prefix = SECONDARY_USER_PREFIX;
+ uid_len = snprintf(NULL, 0, "%d", persona);
+ }
+
+ char *dst = path;
+ size_t dst_size = PKG_PATH_MAX;
+
+ if (append_and_increment(&dst, android_data_dir.path, &dst_size) < 0
+ || append_and_increment(&dst, persona_prefix, &dst_size) < 0) {
+ LOGE("Error building prefix for user path");
+ return -1;
+ }
+
+ if (persona != 0) {
+ if (dst_size < uid_len + 1) {
+ LOGE("Error building user path");
+ return -1;
+ }
+ int ret = snprintf(dst, dst_size, "%d", persona);
+ if (ret < 0 || (size_t) ret != uid_len) {
+ LOGE("Error appending persona id to path");
+ return -1;
+ }
+ }
+ return 0;
+}
+
+/**
* Checks whether the package name is valid. Returns -1 on error and
* 0 on success.
*/
@@ -408,3 +448,35 @@ int append_and_increment(char** dst, const char* src, size_t* dst_size) {
*dst_size -= ret;
return 0;
}
+
+char *build_string2(char *s1, char *s2) {
+ if (s1 == NULL || s2 == NULL) return NULL;
+
+ int len_s1 = strlen(s1);
+ int len_s2 = strlen(s2);
+ int len = len_s1 + len_s2 + 1;
+ char *result = malloc(len);
+ if (result == NULL) return NULL;
+
+ strcpy(result, s1);
+ strcpy(result + len_s1, s2);
+
+ return result;
+}
+
+char *build_string3(char *s1, char *s2, char *s3) {
+ if (s1 == NULL || s2 == NULL || s3 == NULL) return NULL;
+
+ int len_s1 = strlen(s1);
+ int len_s2 = strlen(s2);
+ int len_s3 = strlen(s3);
+ int len = len_s1 + len_s2 + len_s3 + 1;
+ char *result = malloc(len);
+ if (result == NULL) return NULL;
+
+ strcpy(result, s1);
+ strcpy(result + len_s1, s2);
+ strcpy(result + len_s1 + len_s2, s3);
+
+ return result;
+}
diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java
index d058e38..78a450c 100644
--- a/cmds/pm/src/com/android/commands/pm/Pm.java
+++ b/cmds/pm/src/com/android/commands/pm/Pm.java
@@ -35,9 +35,9 @@ import android.content.pm.PermissionInfo;
import android.content.res.AssetManager;
import android.content.res.Resources;
import android.net.Uri;
+import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
-import android.provider.Settings;
import java.io.File;
import java.lang.reflect.Field;
@@ -60,6 +60,7 @@ public final class Pm {
private static final String PM_NOT_RUNNING_ERR =
"Error: Could not access the Package Manager. Is the system running?";
+ private static final int ROOT_UID = 0;
public static void main(String[] args) {
new Pm().run(args);
@@ -127,6 +128,16 @@ public final class Pm {
return;
}
+ if ("createUser".equals(op)) {
+ runCreateUser();
+ return;
+ }
+
+ if ("removeUser".equals(op)) {
+ runRemoveUser();
+ return;
+ }
+
try {
if (args.length == 1) {
if (args[0].equalsIgnoreCase("-l")) {
@@ -763,6 +774,63 @@ public final class Pm {
}
}
+ public void runCreateUser() {
+ // Need to be run as root
+ if (Process.myUid() != ROOT_UID) {
+ System.err.println("Error: createUser must be run as root");
+ return;
+ }
+ String name;
+ String arg = nextArg();
+ if (arg == null) {
+ System.err.println("Error: no user name specified.");
+ showUsage();
+ return;
+ }
+ name = arg;
+ try {
+ if (mPm.createUser(name, 0) == null) {
+ System.err.println("Error: couldn't create user.");
+ showUsage();
+ }
+ } catch (RemoteException e) {
+ System.err.println(e.toString());
+ System.err.println(PM_NOT_RUNNING_ERR);
+ }
+
+ }
+
+ public void runRemoveUser() {
+ // Need to be run as root
+ if (Process.myUid() != ROOT_UID) {
+ System.err.println("Error: removeUser must be run as root");
+ return;
+ }
+ int userId;
+ String arg = nextArg();
+ if (arg == null) {
+ System.err.println("Error: no user id specified.");
+ showUsage();
+ return;
+ }
+ try {
+ userId = Integer.parseInt(arg);
+ } catch (NumberFormatException e) {
+ System.err.println("Error: user id has to be a number.");
+ showUsage();
+ return;
+ }
+ try {
+ if (!mPm.removeUser(userId)) {
+ System.err.println("Error: couldn't remove user.");
+ showUsage();
+ }
+ } catch (RemoteException e) {
+ System.err.println(e.toString());
+ System.err.println(PM_NOT_RUNNING_ERR);
+ }
+ }
+
class PackageDeleteObserver extends IPackageDeleteObserver.Stub {
boolean finished;
boolean result;
@@ -1006,6 +1074,8 @@ public final class Pm {
System.err.println(" pm enable PACKAGE_OR_COMPONENT");
System.err.println(" pm disable PACKAGE_OR_COMPONENT");
System.err.println(" pm setInstallLocation [0/auto] [1/internal] [2/external]");
+ System.err.println(" pm createUser USER_NAME");
+ System.err.println(" pm removeUser USER_ID");
System.err.println("");
System.err.println("The list packages command prints all packages, optionally only");
System.err.println("those whose package name contains the text in FILTER. Options:");
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index ef8ba8e..85918cf 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -1113,7 +1113,11 @@ final class ApplicationPackageManager extends PackageManager {
*/
@Override
public UserInfo createUser(String name, int flags) {
- // TODO
+ try {
+ return mPM.createUser(name, flags);
+ } catch (RemoteException e) {
+ // Should never happen!
+ }
return null;
}
@@ -1136,8 +1140,11 @@ final class ApplicationPackageManager extends PackageManager {
*/
@Override
public boolean removeUser(int id) {
- // TODO:
- return false;
+ try {
+ return mPM.removeUser(id);
+ } catch (RemoteException e) {
+ return false;
+ }
}
/**
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index fbf8f92..11cd446 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -35,6 +35,7 @@ import android.content.pm.PermissionGroupInfo;
import android.content.pm.PermissionInfo;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
+import android.content.pm.UserInfo;
import android.net.Uri;
import android.content.IntentSender;
@@ -329,4 +330,7 @@ interface IPackageManager {
boolean setInstallLocation(int loc);
int getInstallLocation();
+
+ UserInfo createUser(in String name, int flags);
+ boolean removeUser(int userId);
}
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 99c4c7f..ff817c1 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -662,10 +662,15 @@ public abstract class PackageManager {
public static final int MOVE_EXTERNAL_MEDIA = 0x00000002;
/**
- * Feature for {@link #getSystemAvailableFeatures} and
- * {@link #hasSystemFeature}: The device's audio pipeline is low-latency,
- * more suitable for audio applications sensitive to delays or lag in
- * sound input or output.
+ * Range of IDs allocated for a user.
+ * @hide
+ */
+ public static final int PER_USER_RANGE = 100000;
+
+ /**
+ * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}: The device's
+ * audio pipeline is low-latency, more suitable for audio applications sensitive to delays or
+ * lag in sound input or output.
*/
@SdkConstant(SdkConstantType.FEATURE)
public static final String FEATURE_AUDIO_LOW_LATENCY = "android.hardware.audio.low_latency";
@@ -2387,4 +2392,37 @@ public abstract class PackageManager {
* @hide
*/
public abstract void updateUserFlags(int id, int flags);
+
+ /**
+ * Checks to see if the user id is the same for the two uids, i.e., they belong to the same
+ * user.
+ * @hide
+ */
+ public static boolean isSameUser(int uid1, int uid2) {
+ return getUserId(uid1) == getUserId(uid2);
+ }
+
+ /**
+ * Returns the user id for a given uid.
+ * @hide
+ */
+ public static int getUserId(int uid) {
+ return uid / PER_USER_RANGE;
+ }
+
+ /**
+ * Returns the uid that is composed from the userId and the appId.
+ * @hide
+ */
+ public static int getUid(int userId, int appId) {
+ return userId * PER_USER_RANGE + (appId % PER_USER_RANGE);
+ }
+
+ /**
+ * Returns the app id (or base uid) for a given uid, stripping out the user id from it.
+ * @hide
+ */
+ public static int getAppId(int uid) {
+ return uid % PER_USER_RANGE;
+ }
}
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 54a8842..564f4f4 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -24,6 +24,7 @@ import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
+import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
import android.os.PatternMatcher;
diff --git a/core/java/android/content/pm/UserInfo.aidl b/core/java/android/content/pm/UserInfo.aidl
new file mode 100644
index 0000000..2e7cb8f
--- /dev/null
+++ b/core/java/android/content/pm/UserInfo.aidl
@@ -0,0 +1,20 @@
+/*
+**
+** Copyright 2011, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package android.content.pm;
+
+parcelable UserInfo;
diff --git a/core/java/android/content/pm/UserInfo.java b/core/java/android/content/pm/UserInfo.java
index 3704d3a..ba5331c 100644
--- a/core/java/android/content/pm/UserInfo.java
+++ b/core/java/android/content/pm/UserInfo.java
@@ -74,8 +74,7 @@ public class UserInfo implements Parcelable {
@Override
public String toString() {
- return "UserInfo{"
- + id + ":" + name + ":" + Integer.toHexString(flags) + "}";
+ return "UserInfo{" + id + ":" + name + ":" + Integer.toHexString(flags) + "}";
}
public int describeContents() {
diff --git a/core/java/android/speech/tts/FileSynthesisRequest.java b/core/java/android/speech/tts/FileSynthesisRequest.java
index 6a9b2dc..7efc264 100644
--- a/core/java/android/speech/tts/FileSynthesisRequest.java
+++ b/core/java/android/speech/tts/FileSynthesisRequest.java
@@ -45,6 +45,7 @@ class FileSynthesisRequest extends SynthesisRequest {
private int mChannelCount;
private RandomAccessFile mFile;
private boolean mStopped = false;
+ private boolean mDone = false;
FileSynthesisRequest(String text, File fileName) {
super(text);
@@ -89,6 +90,11 @@ class FileSynthesisRequest extends SynthesisRequest {
}
@Override
+ boolean isDone() {
+ return mDone;
+ }
+
+ @Override
public int start(int sampleRateInHz, int audioFormat, int channelCount) {
if (DBG) {
Log.d(TAG, "FileSynthesisRequest.start(" + sampleRateInHz + "," + audioFormat
@@ -164,6 +170,7 @@ class FileSynthesisRequest extends SynthesisRequest {
mFile.write(
makeWavHeader(mSampleRateInHz, mAudioFormat, mChannelCount, dataLength));
closeFile();
+ mDone = true;
return TextToSpeech.SUCCESS;
} catch (IOException ex) {
Log.e(TAG, "Failed to write to " + mFileName + ": " + ex);
@@ -174,6 +181,14 @@ class FileSynthesisRequest extends SynthesisRequest {
}
@Override
+ public void error() {
+ if (DBG) Log.d(TAG, "FileSynthesisRequest.error()");
+ synchronized (mStateLock) {
+ cleanUp();
+ }
+ }
+
+ @Override
public int completeAudioAvailable(int sampleRateInHz, int audioFormat, int channelCount,
byte[] buffer, int offset, int length) {
synchronized (mStateLock) {
@@ -187,9 +202,11 @@ class FileSynthesisRequest extends SynthesisRequest {
out = new FileOutputStream(mFileName);
out.write(makeWavHeader(sampleRateInHz, audioFormat, channelCount, length));
out.write(buffer, offset, length);
+ mDone = true;
return TextToSpeech.SUCCESS;
} catch (IOException ex) {
Log.e(TAG, "Failed to write to " + mFileName + ": " + ex);
+ mFileName.delete();
return TextToSpeech.ERROR;
} finally {
try {
diff --git a/core/java/android/speech/tts/PlaybackSynthesisRequest.java b/core/java/android/speech/tts/PlaybackSynthesisRequest.java
index 2267015..dc5ff70 100644
--- a/core/java/android/speech/tts/PlaybackSynthesisRequest.java
+++ b/core/java/android/speech/tts/PlaybackSynthesisRequest.java
@@ -50,6 +50,7 @@ class PlaybackSynthesisRequest extends SynthesisRequest {
private final Object mStateLock = new Object();
private AudioTrack mAudioTrack = null;
private boolean mStopped = false;
+ private boolean mDone = false;
PlaybackSynthesisRequest(String text, int streamType, float volume, float pan) {
super(text);
@@ -72,7 +73,6 @@ class PlaybackSynthesisRequest extends SynthesisRequest {
if (mAudioTrack != null) {
mAudioTrack.flush();
mAudioTrack.stop();
- // TODO: do we need to wait for playback to finish before releasing?
mAudioTrack.release();
mAudioTrack = null;
}
@@ -85,6 +85,11 @@ class PlaybackSynthesisRequest extends SynthesisRequest {
return MIN_AUDIO_BUFFER_SIZE;
}
+ @Override
+ boolean isDone() {
+ return mDone;
+ }
+
// TODO: add a thread that writes to the AudioTrack?
@Override
public int start(int sampleRateInHz, int audioFormat, int channelCount) {
@@ -104,8 +109,7 @@ class PlaybackSynthesisRequest extends SynthesisRequest {
return TextToSpeech.ERROR;
}
- mAudioTrack = createAudioTrack(sampleRateInHz, audioFormat, channelCount,
- AudioTrack.MODE_STREAM);
+ mAudioTrack = createStreamingAudioTrack(sampleRateInHz, audioFormat, channelCount);
if (mAudioTrack == null) {
return TextToSpeech.ERROR;
}
@@ -183,12 +187,21 @@ class PlaybackSynthesisRequest extends SynthesisRequest {
Log.e(TAG, "done(): Not started");
return TextToSpeech.ERROR;
}
+ mDone = true;
cleanUp();
}
return TextToSpeech.SUCCESS;
}
@Override
+ public void error() {
+ if (DBG) Log.d(TAG, "error()");
+ synchronized (mStateLock) {
+ cleanUp();
+ }
+ }
+
+ @Override
public int completeAudioAvailable(int sampleRateInHz, int audioFormat, int channelCount,
byte[] buffer, int offset, int length) {
if (DBG) {
@@ -208,15 +221,32 @@ class PlaybackSynthesisRequest extends SynthesisRequest {
return TextToSpeech.ERROR;
}
- mAudioTrack = createAudioTrack(sampleRateInHz, audioFormat, channelCount,
- AudioTrack.MODE_STATIC);
+ int channelConfig = getChannelConfig(channelCount);
+ if (channelConfig < 0) {
+ Log.e(TAG, "Unsupported number of channels :" + channelCount);
+ cleanUp();
+ return TextToSpeech.ERROR;
+ }
+ int bytesPerFrame = getBytesPerFrame(audioFormat);
+ if (bytesPerFrame < 0) {
+ Log.e(TAG, "Unsupported audio format :" + audioFormat);
+ cleanUp();
+ return TextToSpeech.ERROR;
+ }
+
+ mAudioTrack = new AudioTrack(mStreamType, sampleRateInHz, channelConfig,
+ audioFormat, buffer.length, AudioTrack.MODE_STATIC);
if (mAudioTrack == null) {
return TextToSpeech.ERROR;
}
try {
mAudioTrack.write(buffer, offset, length);
+ setupVolume(mAudioTrack, mVolume, mPan);
mAudioTrack.play();
+ blockUntilDone(mAudioTrack, bytesPerFrame, length);
+ mDone = true;
+ if (DBG) Log.d(TAG, "Wrote data to audio track succesfully : " + length);
} catch (IllegalStateException ex) {
Log.e(TAG, "Playback error", ex);
return TextToSpeech.ERROR;
@@ -228,15 +258,48 @@ class PlaybackSynthesisRequest extends SynthesisRequest {
return TextToSpeech.SUCCESS;
}
- private AudioTrack createAudioTrack(int sampleRateInHz, int audioFormat, int channelCount,
- int mode) {
- int channelConfig;
+ private void blockUntilDone(AudioTrack audioTrack, int bytesPerFrame, int length) {
+ int lengthInFrames = length / bytesPerFrame;
+ int currentPosition = 0;
+ while ((currentPosition = audioTrack.getPlaybackHeadPosition()) < lengthInFrames) {
+ long estimatedTimeMs = ((lengthInFrames - currentPosition) * 1000) /
+ audioTrack.getSampleRate();
+ if (DBG) Log.d(TAG, "About to sleep for : " + estimatedTimeMs + " ms," +
+ " Playback position : " + currentPosition);
+ try {
+ Thread.sleep(estimatedTimeMs);
+ } catch (InterruptedException ie) {
+ break;
+ }
+ }
+ }
+
+ private int getBytesPerFrame(int audioFormat) {
+ if (audioFormat == AudioFormat.ENCODING_PCM_8BIT) {
+ return 1;
+ } else if (audioFormat == AudioFormat.ENCODING_PCM_16BIT) {
+ return 2;
+ }
+
+ return -1;
+ }
+
+ private int getChannelConfig(int channelCount) {
if (channelCount == 1) {
- channelConfig = AudioFormat.CHANNEL_OUT_MONO;
+ return AudioFormat.CHANNEL_OUT_MONO;
} else if (channelCount == 2){
- channelConfig = AudioFormat.CHANNEL_OUT_STEREO;
- } else {
- Log.e(TAG, "Unsupported number of channels: " + channelCount);
+ return AudioFormat.CHANNEL_OUT_STEREO;
+ }
+
+ return -1;
+ }
+
+ private AudioTrack createStreamingAudioTrack(int sampleRateInHz, int audioFormat,
+ int channelCount) {
+ int channelConfig = getChannelConfig(channelCount);
+
+ if (channelConfig < 0) {
+ Log.e(TAG, "Unsupported number of channels : " + channelCount);
return null;
}
@@ -244,10 +307,11 @@ class PlaybackSynthesisRequest extends SynthesisRequest {
= AudioTrack.getMinBufferSize(sampleRateInHz, channelConfig, audioFormat);
int bufferSizeInBytes = Math.max(MIN_AUDIO_BUFFER_SIZE, minBufferSizeInBytes);
AudioTrack audioTrack = new AudioTrack(mStreamType, sampleRateInHz, channelConfig,
- audioFormat, bufferSizeInBytes, mode);
+ audioFormat, bufferSizeInBytes, AudioTrack.MODE_STREAM);
if (audioTrack == null) {
return null;
}
+
if (audioTrack.getState() != AudioTrack.STATE_INITIALIZED) {
audioTrack.release();
return null;
@@ -255,4 +319,4 @@ class PlaybackSynthesisRequest extends SynthesisRequest {
setupVolume(audioTrack, mVolume, mPan);
return audioTrack;
}
-} \ No newline at end of file
+}
diff --git a/core/java/android/speech/tts/SynthesisRequest.java b/core/java/android/speech/tts/SynthesisRequest.java
index f4bb852..515218b 100644
--- a/core/java/android/speech/tts/SynthesisRequest.java
+++ b/core/java/android/speech/tts/SynthesisRequest.java
@@ -114,6 +114,11 @@ public abstract class SynthesisRequest {
public abstract int getMaxBufferSize();
/**
+ * Checks whether the synthesis request completed successfully.
+ */
+ abstract boolean isDone();
+
+ /**
* Aborts the speech request.
*
* Can be called from multiple threads.
@@ -162,6 +167,14 @@ public abstract class SynthesisRequest {
public abstract int done();
/**
+ * The service should call this method if the speech synthesis fails.
+ *
+ * This method should only be called on the synthesis thread,
+ * while in {@link TextToSpeechService#onSynthesizeText}.
+ */
+ public abstract void error();
+
+ /**
* The service can call this method instead of using {@link #start}, {@link #audioAvailable}
* and {@link #done} if all the audio data is available in a single buffer.
*
diff --git a/core/java/android/speech/tts/TextToSpeechService.java b/core/java/android/speech/tts/TextToSpeechService.java
index a408ea2..da97fb4 100644
--- a/core/java/android/speech/tts/TextToSpeechService.java
+++ b/core/java/android/speech/tts/TextToSpeechService.java
@@ -150,12 +150,10 @@ public abstract class TextToSpeechService extends Service {
*
* Called on the synthesis thread.
*
- * @param request The synthesis request. The method should
- * call {@link SynthesisRequest#start}, {@link SynthesisRequest#audioAvailable},
- * and {@link SynthesisRequest#done} on this request.
- * @return {@link TextToSpeech#SUCCESS} or {@link TextToSpeech#ERROR}.
+ * @param request The synthesis request. The method should use the methods in the request
+ * object to communicate the results of the synthesis.
*/
- protected abstract int onSynthesizeText(SynthesisRequest request);
+ protected abstract void onSynthesizeText(SynthesisRequest request);
private boolean areDefaultsEnforced() {
return getSecureSettingInt(Settings.Secure.TTS_USE_DEFAULTS,
@@ -442,7 +440,8 @@ public abstract class TextToSpeechService extends Service {
synthesisRequest = mSynthesisRequest;
}
setRequestParams(synthesisRequest);
- return TextToSpeechService.this.onSynthesizeText(synthesisRequest);
+ TextToSpeechService.this.onSynthesizeText(synthesisRequest);
+ return synthesisRequest.isDone() ? TextToSpeech.SUCCESS : TextToSpeech.ERROR;
}
protected SynthesisRequest createSynthesisRequest() {
diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java
index af954c9..9d29a60 100644
--- a/core/java/android/widget/ListView.java
+++ b/core/java/android/widget/ListView.java
@@ -251,7 +251,7 @@ public class ListView extends AbsListView {
*/
public void addHeaderView(View v, Object data, boolean isSelectable) {
- if (mAdapter != null) {
+ if (mAdapter != null && ! (mAdapter instanceof HeaderViewListAdapter)) {
throw new IllegalStateException(
"Cannot add header view to list -- setAdapter has already been called.");
}
@@ -261,6 +261,12 @@ public class ListView extends AbsListView {
info.data = data;
info.isSelectable = isSelectable;
mHeaderViewInfos.add(info);
+
+ // in the case of re-adding a header view, or adding one later on,
+ // we need to notify the observer
+ if (mDataSetObserver != null) {
+ mDataSetObserver.onChanged();
+ }
}
/**
@@ -294,7 +300,9 @@ public class ListView extends AbsListView {
if (mHeaderViewInfos.size() > 0) {
boolean result = false;
if (((HeaderViewListAdapter) mAdapter).removeHeader(v)) {
- mDataSetObserver.onChanged();
+ if (mDataSetObserver != null) {
+ mDataSetObserver.onChanged();
+ }
result = true;
}
removeFixedViewInfo(v, mHeaderViewInfos);
@@ -328,6 +336,12 @@ public class ListView extends AbsListView {
* @param isSelectable true if the footer view can be selected
*/
public void addFooterView(View v, Object data, boolean isSelectable) {
+
+ if (mAdapter != null && ! (mAdapter instanceof HeaderViewListAdapter)) {
+ throw new IllegalStateException(
+ "Cannot add footer view to list -- setAdapter has already been called.");
+ }
+
FixedViewInfo info = new FixedViewInfo();
info.view = v;
info.data = data;
@@ -371,7 +385,9 @@ public class ListView extends AbsListView {
if (mFooterViewInfos.size() > 0) {
boolean result = false;
if (((HeaderViewListAdapter) mAdapter).removeFooter(v)) {
- mDataSetObserver.onChanged();
+ if (mDataSetObserver != null) {
+ mDataSetObserver.onChanged();
+ }
result = true;
}
removeFixedViewInfo(v, mFooterViewInfos);
diff --git a/core/jni/android/graphics/Canvas.cpp b/core/jni/android/graphics/Canvas.cpp
index 207c72f..310f02f 100644
--- a/core/jni/android/graphics/Canvas.cpp
+++ b/core/jni/android/graphics/Canvas.cpp
@@ -743,7 +743,11 @@ public:
jcharArray text, int index, int count,
jfloat x, jfloat y, int flags, SkPaint* paint) {
jchar* textArray = env->GetCharArrayElements(text, NULL);
+#if RTL_USE_HARFBUZZ
+ drawTextWithGlyphs(canvas, textArray + index, 0, count, x, y, flags, paint);
+#else
TextLayout::drawText(paint, textArray + index, count, flags, x, y, canvas);
+#endif
env->ReleaseCharArrayElements(text, textArray, JNI_ABORT);
}
@@ -752,7 +756,11 @@ public:
int start, int end,
jfloat x, jfloat y, int flags, SkPaint* paint) {
const jchar* textArray = env->GetStringChars(text, NULL);
+#if RTL_USE_HARFBUZZ
+ drawTextWithGlyphs(canvas, textArray, start, end, x, y, flags, paint);
+#else
TextLayout::drawText(paint, textArray + start, end - start, flags, x, y, canvas);
+#endif
env->ReleaseStringChars(text, textArray);
}
@@ -781,6 +789,23 @@ public:
x, y, flags, paint);
}
+ static void drawTextWithGlyphs(SkCanvas* canvas, const jchar* textArray,
+ int start, int count, int contextCount,
+ jfloat x, jfloat y, int flags, SkPaint* paint) {
+
+ sp<TextLayoutCacheValue> value = gTextLayoutCache.getValue(
+ paint, textArray, start, count, contextCount, flags);
+ if (value == NULL) {
+ LOGE("drawTextWithGlyphs -- cannot get Cache value");
+ return ;
+ }
+#if DEBUG_GLYPHS
+ logGlyphs(value);
+#endif
+ doDrawGlyphs(canvas, value->getGlyphs(), 0, value->getGlyphsCount(),
+ x, y, flags, paint);
+ }
+
static void drawTextWithGlyphs___CIIFFIPaint(JNIEnv* env, jobject, SkCanvas* canvas,
jcharArray text, int index, int count,
jfloat x, jfloat y, int flags, SkPaint* paint) {
@@ -831,8 +856,13 @@ public:
jfloat x, jfloat y, int dirFlags, SkPaint* paint) {
jchar* chars = env->GetCharArrayElements(text, NULL);
+#if RTL_USE_HARFBUZZ
+ drawTextWithGlyphs(canvas, chars + contextIndex, index - contextIndex,
+ count, contextCount, x, y, dirFlags, paint);
+#else
TextLayout::drawTextRun(paint, chars + contextIndex, index - contextIndex,
- count, contextCount, dirFlags, x, y, canvas);
+ count, contextCount, dirFlags, x, y, canvas);
+#endif
env->ReleaseCharArrayElements(text, chars, JNI_ABORT);
}
@@ -844,8 +874,13 @@ public:
jint count = end - start;
jint contextCount = contextEnd - contextStart;
const jchar* chars = env->GetStringChars(text, NULL);
+#if RTL_USE_HARFBUZZ
+ drawTextWithGlyphs(canvas, chars + contextStart, start - contextStart,
+ count, contextCount, x, y, dirFlags, paint);
+#else
TextLayout::drawTextRun(paint, chars + contextStart, start - contextStart,
- count, contextCount, dirFlags, x, y, canvas);
+ count, contextCount, dirFlags, x, y, canvas);
+#endif
env->ReleaseStringChars(text, chars);
}
diff --git a/core/jni/android/graphics/RtlProperties.h b/core/jni/android/graphics/RtlProperties.h
index 4fac89a..a41c91b 100644
--- a/core/jni/android/graphics/RtlProperties.h
+++ b/core/jni/android/graphics/RtlProperties.h
@@ -52,7 +52,7 @@ static RtlDebugLevel readRtlDebugLevel() {
#define DEBUG_ADVANCES 0
// Define if we want (1) to have Glyphs debug values or not (0)
-#define DEBUG_GLYPHS 1
+#define DEBUG_GLYPHS 0
} // namespace android
#endif // ANDROID_RTL_PROPERTIES_H
diff --git a/core/jni/android/graphics/TextLayoutCache.cpp b/core/jni/android/graphics/TextLayoutCache.cpp
index 088202e..77a731a 100644
--- a/core/jni/android/graphics/TextLayoutCache.cpp
+++ b/core/jni/android/graphics/TextLayoutCache.cpp
@@ -420,7 +420,9 @@ void TextLayoutCacheValue::computeValuesWithHarfbuzz(SkPaint* paint, const UChar
UBiDi* bidi = ubidi_open();
if (bidi) {
UErrorCode status = U_ZERO_ERROR;
+#if DEBUG_GLYPHS
LOGD("computeValuesWithHarfbuzz -- bidiReq=%d", bidiReq);
+#endif
ubidi_setPara(bidi, chars, contextCount, bidiReq, NULL, &status);
if (U_SUCCESS(status)) {
int paraDir = ubidi_getParaLevel(bidi) & kDirection_Mask; // 0 if ltr, 1 if rtl
@@ -430,7 +432,6 @@ void TextLayoutCacheValue::computeValuesWithHarfbuzz(SkPaint* paint, const UChar
#endif
if (rc == 1 || !U_SUCCESS(status)) {
- LOGD("HERE !!!");
computeRunValuesWithHarfbuzz(paint, chars, start, count, contextCount,
dirFlags, outAdvances, outTotalAdvance, outGlyphs, outGlyphsCount);
ubidi_close(bidi);
@@ -517,16 +518,25 @@ void TextLayoutCacheValue::computeRunValuesWithHarfbuzz(SkPaint* paint, const UC
#endif
// Get Advances and their total
- jfloat totalAdvance = 0;
- for (size_t i = 0; i < count; i++) {
- totalAdvance += outAdvances[i] = HBFixedToFloat(shaperItem.advances[i]);
-#if DEBUG_ADVANCES
- LOGD("hb-adv = %d - rebased = %f - total = %f", shaperItem.advances[i], outAdvances[i],
- totalAdvance);
-#endif
+ jfloat totalAdvance = outAdvances[0] = HBFixedToFloat(shaperItem.advances[shaperItem.log_clusters[0]]);
+ for (size_t i = 1; i < count; i++) {
+ size_t clusterPrevious = shaperItem.log_clusters[i - 1];
+ size_t cluster = shaperItem.log_clusters[i];
+ if (cluster == clusterPrevious) {
+ outAdvances[i] = 0;
+ } else {
+ totalAdvance += outAdvances[i] = HBFixedToFloat(shaperItem.advances[shaperItem.log_clusters[i]]);
+ }
}
*outTotalAdvance = totalAdvance;
+#if DEBUG_ADVANCES
+ for (size_t i = 0; i < count; i++) {
+ LOGD("hb-adv[%d] = %f - log_clusters = %d - total = %f", i,
+ outAdvances[i], shaperItem.log_clusters[i], totalAdvance);
+ }
+#endif
+
// Get Glyphs
if (outGlyphs) {
*outGlyphsCount = shaperItem.num_glyphs;
diff --git a/include/gui/SurfaceTexture.h b/include/gui/SurfaceTexture.h
index 340daaf..96828c6 100644
--- a/include/gui/SurfaceTexture.h
+++ b/include/gui/SurfaceTexture.h
@@ -127,11 +127,28 @@ public:
// be called from the client.
status_t setDefaultBufferSize(uint32_t w, uint32_t h);
-private:
+ // getCurrentBuffer returns the buffer associated with the current image.
+ sp<GraphicBuffer> getCurrentBuffer() const;
+
+ // getCurrentTextureTarget returns the texture target of the current
+ // texture as returned by updateTexImage().
+ GLenum getCurrentTextureTarget() const;
+
+ // getCurrentCrop returns the cropping rectangle of the current buffer
+ Rect getCurrentCrop() const;
+
+ // getCurrentTransform returns the transform of the current buffer
+ uint32_t getCurrentTransform() const;
+
+protected:
// freeAllBuffers frees the resources (both GraphicBuffer and EGLImage) for
// all slots.
void freeAllBuffers();
+ static bool isExternalFormat(uint32_t format);
+ static GLenum getTextureTarget(uint32_t format);
+
+private:
// createImage creates a new EGLImage from a GraphicBuffer.
EGLImageKHR createImage(EGLDisplay dpy,
@@ -194,6 +211,10 @@ private:
// reset mCurrentTexture to INVALID_BUFFER_SLOT.
int mCurrentTexture;
+ // mCurrentTextureTarget is the GLES texture target to be used with the
+ // current texture.
+ GLenum mCurrentTextureTarget;
+
// mCurrentTextureBuf is the graphic buffer of the current texture. It's
// possible that this buffer is not associated with any buffer slot, so we
// must track it separately in order to properly use
@@ -256,7 +277,7 @@ private:
// mMutex is the mutex used to prevent concurrent access to the member
// variables of SurfaceTexture objects. It must be locked whenever the
// member variables are accessed.
- Mutex mMutex;
+ mutable Mutex mMutex;
};
// ----------------------------------------------------------------------------
diff --git a/include/gui/SurfaceTextureClient.h b/include/gui/SurfaceTextureClient.h
index df82bf2..fe9b049 100644
--- a/include/gui/SurfaceTextureClient.h
+++ b/include/gui/SurfaceTextureClient.h
@@ -27,6 +27,8 @@
namespace android {
+class Surface;
+
class SurfaceTextureClient
: public EGLNativeBase<ANativeWindow, SurfaceTextureClient, RefBase>
{
@@ -36,6 +38,7 @@ public:
sp<ISurfaceTexture> getISurfaceTexture() const;
private:
+ friend class Surface;
// can't be copied
SurfaceTextureClient& operator = (const SurfaceTextureClient& rhs);
@@ -78,6 +81,8 @@ private:
void freeAllBuffers();
+ int getConnectedApi() const;
+
enum { MIN_UNDEQUEUED_BUFFERS = SurfaceTexture::MIN_UNDEQUEUED_BUFFERS };
enum { MIN_BUFFER_SLOTS = SurfaceTexture::MIN_BUFFER_SLOTS };
enum { NUM_BUFFER_SLOTS = SurfaceTexture::NUM_BUFFER_SLOTS };
@@ -121,10 +126,25 @@ private:
// a timestamp is auto-generated when queueBuffer is called.
int64_t mTimestamp;
+ // mConnectedApi holds the currently connected API to this surface
+ int mConnectedApi;
+
+ // mQueryWidth is the width returned by query(). It is set to width
+ // of the last dequeued buffer or to mReqWidth if no buffer was dequeued.
+ uint32_t mQueryWidth;
+
+ // mQueryHeight is the height returned by query(). It is set to height
+ // of the last dequeued buffer or to mReqHeight if no buffer was dequeued.
+ uint32_t mQueryHeight;
+
+ // mQueryFormat is the format returned by query(). It is set to the last
+ // dequeued format or to mReqFormat if no buffer was dequeued.
+ uint32_t mQueryFormat;
+
// mMutex is the mutex used to prevent concurrent access to the member
// variables of SurfaceTexture objects. It must be locked whenever the
// member variables are accessed.
- Mutex mMutex;
+ mutable Mutex mMutex;
};
}; // namespace android
diff --git a/libs/gui/SurfaceTexture.cpp b/libs/gui/SurfaceTexture.cpp
index e2346f0..39418f0 100644
--- a/libs/gui/SurfaceTexture.cpp
+++ b/libs/gui/SurfaceTexture.cpp
@@ -27,6 +27,8 @@
#include <gui/SurfaceTexture.h>
+#include <hardware/hardware.h>
+
#include <surfaceflinger/ISurfaceComposer.h>
#include <surfaceflinger/SurfaceComposerClient.h>
#include <surfaceflinger/IGraphicBufferAlloc.h>
@@ -82,6 +84,7 @@ SurfaceTexture::SurfaceTexture(GLuint tex) :
mUseDefaultSize(true),
mBufferCount(MIN_BUFFER_SLOTS),
mCurrentTexture(INVALID_BUFFER_SLOT),
+ mCurrentTextureTarget(GL_TEXTURE_EXTERNAL_OES),
mCurrentTransform(0),
mCurrentTimestamp(0),
mLastQueued(INVALID_BUFFER_SLOT),
@@ -197,6 +200,7 @@ status_t SurfaceTexture::dequeueBuffer(int *buf) {
if (buffer == NULL) {
return ISurfaceTexture::BUFFER_NEEDS_REALLOCATION;
}
+
if ((mUseDefaultSize) &&
((uint32_t(buffer->width) != mDefaultWidth) ||
(uint32_t(buffer->height) != mDefaultHeight))) {
@@ -263,9 +267,6 @@ status_t SurfaceTexture::updateTexImage() {
LOGV("SurfaceTexture::updateTexImage");
Mutex::Autolock lock(mMutex);
- // We always bind the texture even if we don't update its contents.
- glBindTexture(GL_TEXTURE_EXTERNAL_OES, mTexName);
-
// Initially both mCurrentTexture and mLastQueued are INVALID_BUFFER_SLOT,
// so this check will fail until a buffer gets queued.
if (mCurrentTexture != mLastQueued) {
@@ -283,7 +284,15 @@ status_t SurfaceTexture::updateTexImage() {
while ((error = glGetError()) != GL_NO_ERROR) {
LOGE("GL error cleared before updating SurfaceTexture: %#04x", error);
}
- glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, (GLeglImageOES)image);
+
+ GLenum target = getTextureTarget(
+ mSlots[mLastQueued].mGraphicBuffer->format);
+ if (target != mCurrentTextureTarget) {
+ glDeleteTextures(1, &mTexName);
+ }
+ glBindTexture(target, mTexName);
+ glEGLImageTargetTexture2DOES(target, (GLeglImageOES)image);
+
bool failed = false;
while ((error = glGetError()) != GL_NO_ERROR) {
LOGE("error binding external texture image %p (slot %d): %#04x",
@@ -296,14 +305,53 @@ status_t SurfaceTexture::updateTexImage() {
// Update the SurfaceTexture state.
mCurrentTexture = mLastQueued;
+ mCurrentTextureTarget = target;
mCurrentTextureBuf = mSlots[mCurrentTexture].mGraphicBuffer;
mCurrentCrop = mLastQueuedCrop;
mCurrentTransform = mLastQueuedTransform;
mCurrentTimestamp = mLastQueuedTimestamp;
+ } else {
+ // We always bind the texture even if we don't update its contents.
+ glBindTexture(mCurrentTextureTarget, mTexName);
}
return OK;
}
+bool SurfaceTexture::isExternalFormat(uint32_t format)
+{
+ switch (format) {
+ // supported YUV formats
+ case HAL_PIXEL_FORMAT_YV12:
+ // Legacy/deprecated YUV formats
+ case HAL_PIXEL_FORMAT_YCbCr_422_SP:
+ case HAL_PIXEL_FORMAT_YCrCb_420_SP:
+ case HAL_PIXEL_FORMAT_YCbCr_422_I:
+ return true;
+ }
+
+ // Any OEM format needs to be considered
+ if (format>=0x100 && format<=0x1FF)
+ return true;
+
+ return false;
+}
+
+GLenum SurfaceTexture::getTextureTarget(uint32_t format)
+{
+ GLenum target = GL_TEXTURE_2D;
+#if defined(GL_OES_EGL_image_external)
+ if (isExternalFormat(format)) {
+ target = GL_TEXTURE_EXTERNAL_OES;
+ }
+#endif
+ return target;
+}
+
+GLenum SurfaceTexture::getCurrentTextureTarget() const {
+ Mutex::Autolock lock(mMutex);
+ return mCurrentTextureTarget;
+}
+
void SurfaceTexture::getTransformMatrix(float mtx[16]) {
LOGV("SurfaceTexture::getTransformMatrix");
Mutex::Autolock lock(mMutex);
@@ -445,6 +493,22 @@ EGLImageKHR SurfaceTexture::createImage(EGLDisplay dpy,
return image;
}
+sp<GraphicBuffer> SurfaceTexture::getCurrentBuffer() const {
+ Mutex::Autolock lock(mMutex);
+ return mCurrentTextureBuf;
+}
+
+Rect SurfaceTexture::getCurrentCrop() const {
+ Mutex::Autolock lock(mMutex);
+ return mCurrentCrop;
+}
+
+uint32_t SurfaceTexture::getCurrentTransform() const {
+ Mutex::Autolock lock(mMutex);
+ return mCurrentTransform;
+}
+
+
static void mtxMul(float out[16], const float a[16], const float b[16]) {
out[0] = a[0]*b[0] + a[4]*b[1] + a[8]*b[2] + a[12]*b[3];
out[1] = a[1]*b[0] + a[5]*b[1] + a[9]*b[2] + a[13]*b[3];
diff --git a/libs/gui/SurfaceTextureClient.cpp b/libs/gui/SurfaceTextureClient.cpp
index 29fc4d3..f4b2416 100644
--- a/libs/gui/SurfaceTextureClient.cpp
+++ b/libs/gui/SurfaceTextureClient.cpp
@@ -26,8 +26,10 @@ namespace android {
SurfaceTextureClient::SurfaceTextureClient(
const sp<ISurfaceTexture>& surfaceTexture):
mSurfaceTexture(surfaceTexture), mAllocator(0), mReqWidth(0),
- mReqHeight(0), mReqFormat(DEFAULT_FORMAT), mReqUsage(0),
- mTimestamp(NATIVE_WINDOW_TIMESTAMP_AUTO), mMutex() {
+ mReqHeight(0), mReqFormat(0), mReqUsage(0),
+ mTimestamp(NATIVE_WINDOW_TIMESTAMP_AUTO), mConnectedApi(0),
+ mQueryWidth(0), mQueryHeight(0), mQueryFormat(0),
+ mMutex() {
// Initialize the ANativeWindow function pointers.
ANativeWindow::setSwapInterval = setSwapInterval;
ANativeWindow::dequeueBuffer = dequeueBuffer;
@@ -101,9 +103,10 @@ int SurfaceTextureClient::dequeueBuffer(android_native_buffer_t** buffer) {
}
sp<GraphicBuffer>& gbuf(mSlots[buf]);
if (err == ISurfaceTexture::BUFFER_NEEDS_REALLOCATION ||
- gbuf == 0 || gbuf->getWidth() != mReqWidth ||
- gbuf->getHeight() != mReqHeight ||
- uint32_t(gbuf->getPixelFormat()) != mReqFormat ||
+ gbuf == 0 ||
+ (mReqWidth && gbuf->getWidth() != mReqWidth) ||
+ (mReqHeight && gbuf->getHeight() != mReqHeight) ||
+ (mReqFormat && uint32_t(gbuf->getPixelFormat()) != mReqFormat) ||
(gbuf->getUsage() & mReqUsage) != mReqUsage) {
gbuf = mSurfaceTexture->requestBuffer(buf, mReqWidth, mReqHeight,
mReqFormat, mReqUsage);
@@ -111,6 +114,9 @@ int SurfaceTextureClient::dequeueBuffer(android_native_buffer_t** buffer) {
LOGE("dequeueBuffer: ISurfaceTexture::requestBuffer failed");
return NO_MEMORY;
}
+ mQueryWidth = gbuf->width;
+ mQueryHeight = gbuf->height;
+ mQueryFormat = gbuf->format;
}
*buffer = gbuf.get();
return OK;
@@ -159,13 +165,13 @@ int SurfaceTextureClient::query(int what, int* value) {
Mutex::Autolock lock(mMutex);
switch (what) {
case NATIVE_WINDOW_WIDTH:
+ *value = mQueryWidth ? mQueryWidth : mReqWidth;
+ return NO_ERROR;
case NATIVE_WINDOW_HEIGHT:
- // XXX: How should SurfaceTexture behave if setBuffersGeometry didn't
- // override the size?
- *value = 0;
+ *value = mQueryHeight ? mQueryHeight : mReqHeight;
return NO_ERROR;
case NATIVE_WINDOW_FORMAT:
- *value = DEFAULT_FORMAT;
+ *value = mQueryFormat ? mQueryFormat : mReqFormat;
return NO_ERROR;
case NATIVE_WINDOW_MIN_UNDEQUEUED_BUFFERS:
*value = MIN_UNDEQUEUED_BUFFERS;
@@ -260,16 +266,49 @@ int SurfaceTextureClient::dispatchSetBuffersTimestamp(va_list args) {
int SurfaceTextureClient::connect(int api) {
LOGV("SurfaceTextureClient::connect");
- // XXX: Implement this!
- return INVALID_OPERATION;
+ Mutex::Autolock lock(mMutex);
+ int err = NO_ERROR;
+ switch (api) {
+ case NATIVE_WINDOW_API_EGL:
+ if (mConnectedApi) {
+ err = -EINVAL;
+ } else {
+ mConnectedApi = api;
+ }
+ break;
+ default:
+ err = -EINVAL;
+ break;
+ }
+ return err;
}
int SurfaceTextureClient::disconnect(int api) {
LOGV("SurfaceTextureClient::disconnect");
- // XXX: Implement this!
- return INVALID_OPERATION;
+ Mutex::Autolock lock(mMutex);
+ int err = NO_ERROR;
+ switch (api) {
+ case NATIVE_WINDOW_API_EGL:
+ if (mConnectedApi == api) {
+ mConnectedApi = 0;
+ } else {
+ err = -EINVAL;
+ }
+ break;
+ default:
+ err = -EINVAL;
+ break;
+ }
+ return err;
}
+int SurfaceTextureClient::getConnectedApi() const
+{
+ Mutex::Autolock lock(mMutex);
+ return mConnectedApi;
+}
+
+
int SurfaceTextureClient::setUsage(uint32_t reqUsage)
{
LOGV("SurfaceTextureClient::setUsage");
diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp
index 1ca2d6d..f9db1a1 100644
--- a/media/libstagefright/MPEG4Extractor.cpp
+++ b/media/libstagefright/MPEG4Extractor.cpp
@@ -377,7 +377,7 @@ status_t MPEG4Extractor::readMetaData() {
mFileMetaData->setCString(kKeyMIMEType, "audio/mp4");
}
- mInitCheck = verifyIfStreamable();
+ mInitCheck = OK;
} else {
mInitCheck = err;
}
@@ -1904,7 +1904,7 @@ status_t MPEG4Source::read(
off64_t offset;
size_t size;
- uint32_t dts;
+ uint32_t cts;
bool isSyncSample;
bool newBuffer = false;
if (mBuffer == NULL) {
@@ -1912,7 +1912,7 @@ status_t MPEG4Source::read(
status_t err =
mSampleTable->getMetaDataForSample(
- mCurrentSampleIndex, &offset, &size, &dts, &isSyncSample);
+ mCurrentSampleIndex, &offset, &size, &cts, &isSyncSample);
if (err != OK) {
return err;
@@ -1942,7 +1942,7 @@ status_t MPEG4Source::read(
mBuffer->set_range(0, size);
mBuffer->meta_data()->clear();
mBuffer->meta_data()->setInt64(
- kKeyTime, ((int64_t)dts * 1000000) / mTimescale);
+ kKeyTime, ((int64_t)cts * 1000000) / mTimescale);
if (targetSampleTimeUs >= 0) {
mBuffer->meta_data()->setInt64(
@@ -2060,7 +2060,7 @@ status_t MPEG4Source::read(
mBuffer->meta_data()->clear();
mBuffer->meta_data()->setInt64(
- kKeyTime, ((int64_t)dts * 1000000) / mTimescale);
+ kKeyTime, ((int64_t)cts * 1000000) / mTimescale);
if (targetSampleTimeUs >= 0) {
mBuffer->meta_data()->setInt64(
@@ -2094,87 +2094,6 @@ MPEG4Extractor::Track *MPEG4Extractor::findTrackByMimePrefix(
return NULL;
}
-status_t MPEG4Extractor::verifyIfStreamable() {
- if (!(mDataSource->flags() & DataSource::kIsCachingDataSource)) {
- return OK;
- }
-
- Track *audio = findTrackByMimePrefix("audio/");
- Track *video = findTrackByMimePrefix("video/");
-
- if (audio == NULL || video == NULL) {
- return OK;
- }
-
- sp<SampleTable> audioSamples = audio->sampleTable;
- sp<SampleTable> videoSamples = video->sampleTable;
-
- off64_t maxOffsetDiff = 0;
- int64_t maxOffsetTimeUs = -1;
-
- for (uint32_t i = 0; i < videoSamples->countSamples(); ++i) {
- off64_t videoOffset;
- uint32_t videoTime;
- bool isSync;
- CHECK_EQ((status_t)OK, videoSamples->getMetaDataForSample(
- i, &videoOffset, NULL, &videoTime, &isSync));
-
- int64_t videoTimeUs = (int64_t)(videoTime * 1E6 / video->timescale);
-
- uint32_t reqAudioTime = (videoTimeUs * audio->timescale) / 1000000;
- uint32_t j;
- if (audioSamples->findSampleAtTime(
- reqAudioTime, &j, SampleTable::kFlagClosest) != OK) {
- continue;
- }
-
- off64_t audioOffset;
- uint32_t audioTime;
- CHECK_EQ((status_t)OK, audioSamples->getMetaDataForSample(
- j, &audioOffset, NULL, &audioTime));
-
- int64_t audioTimeUs = (int64_t)(audioTime * 1E6 / audio->timescale);
-
- off64_t offsetDiff = videoOffset - audioOffset;
- if (offsetDiff < 0) {
- offsetDiff = -offsetDiff;
- }
-
-#if 0
- printf("%s%d/%d videoTime %.2f secs audioTime %.2f secs "
- "videoOffset %lld audioOffset %lld offsetDiff %lld\n",
- isSync ? "*" : " ",
- i,
- j,
- videoTimeUs / 1E6,
- audioTimeUs / 1E6,
- videoOffset,
- audioOffset,
- offsetDiff);
-#endif
-
- if (offsetDiff > maxOffsetDiff) {
- maxOffsetDiff = offsetDiff;
- maxOffsetTimeUs = videoTimeUs;
- }
- }
-
-#if 0
- printf("max offset diff: %lld at video time: %.2f secs\n",
- maxOffsetDiff, maxOffsetTimeUs / 1E6);
-#endif
-
- if (maxOffsetDiff < 1024 * 1024) {
- return OK;
- }
-
- LOGE("This content is not streamable, "
- "max offset diff: %lld at video time: %.2f secs",
- maxOffsetDiff, maxOffsetTimeUs / 1E6);
-
- return ERROR_UNSUPPORTED;
-}
-
static bool LegacySniffMPEG4(
const sp<DataSource> &source, String8 *mimeType, float *confidence) {
uint8_t header[8];
diff --git a/media/libstagefright/NuCachedSource2.cpp b/media/libstagefright/NuCachedSource2.cpp
index c1aa46e..dc86885 100644
--- a/media/libstagefright/NuCachedSource2.cpp
+++ b/media/libstagefright/NuCachedSource2.cpp
@@ -323,25 +323,28 @@ void NuCachedSource2::onRead(const sp<AMessage> &msg) {
}
void NuCachedSource2::restartPrefetcherIfNecessary_l(
- bool ignoreLowWaterThreshold) {
+ bool ignoreLowWaterThreshold, bool force) {
static const size_t kGrayArea = 1024 * 1024;
if (mFetching || mFinalStatus != OK) {
return;
}
- if (!ignoreLowWaterThreshold
+ if (!ignoreLowWaterThreshold && !force
&& mCacheOffset + mCache->totalSize() - mLastAccessPos
>= kLowWaterThreshold) {
return;
}
size_t maxBytes = mLastAccessPos - mCacheOffset;
- if (maxBytes < kGrayArea) {
- return;
- }
- maxBytes -= kGrayArea;
+ if (!force) {
+ if (maxBytes < kGrayArea) {
+ return;
+ }
+
+ maxBytes -= kGrayArea;
+ }
size_t actualBytes = mCache->releaseFromStart(maxBytes);
mCacheOffset += actualBytes;
@@ -413,10 +416,19 @@ size_t NuCachedSource2::approxDataRemaining_l(status_t *finalStatus) {
}
ssize_t NuCachedSource2::readInternal(off64_t offset, void *data, size_t size) {
+ CHECK_LE(size, (size_t)kHighWaterThreshold);
+
LOGV("readInternal offset %lld size %d", offset, size);
Mutex::Autolock autoLock(mLock);
+ if (!mFetching) {
+ mLastAccessPos = offset;
+ restartPrefetcherIfNecessary_l(
+ false, // ignoreLowWaterThreshold
+ true); // force
+ }
+
if (offset < mCacheOffset
|| offset >= (off64_t)(mCacheOffset + mCache->totalSize())) {
static const off64_t kPadding = 256 * 1024;
diff --git a/media/libstagefright/SampleTable.cpp b/media/libstagefright/SampleTable.cpp
index 423df70..08db902 100644
--- a/media/libstagefright/SampleTable.cpp
+++ b/media/libstagefright/SampleTable.cpp
@@ -53,6 +53,7 @@ SampleTable::SampleTable(const sp<DataSource> &source)
mNumSampleSizes(0),
mTimeToSampleCount(0),
mTimeToSample(NULL),
+ mSampleTimeEntries(NULL),
mCompositionTimeDeltaEntries(NULL),
mNumCompositionTimeDeltaEntries(0),
mSyncSampleOffset(-1),
@@ -73,6 +74,9 @@ SampleTable::~SampleTable() {
delete[] mCompositionTimeDeltaEntries;
mCompositionTimeDeltaEntries = NULL;
+ delete[] mSampleTimeEntries;
+ mSampleTimeEntries = NULL;
+
delete[] mTimeToSample;
mTimeToSample = NULL;
@@ -381,67 +385,128 @@ uint32_t abs_difference(uint32_t time1, uint32_t time2) {
return time1 > time2 ? time1 - time2 : time2 - time1;
}
-status_t SampleTable::findSampleAtTime(
- uint32_t req_time, uint32_t *sample_index, uint32_t flags) {
- // XXX this currently uses decoding time, instead of composition time.
+// static
+int SampleTable::CompareIncreasingTime(const void *_a, const void *_b) {
+ const SampleTimeEntry *a = (const SampleTimeEntry *)_a;
+ const SampleTimeEntry *b = (const SampleTimeEntry *)_b;
- *sample_index = 0;
+ if (a->mCompositionTime < b->mCompositionTime) {
+ return -1;
+ } else if (a->mCompositionTime > b->mCompositionTime) {
+ return 1;
+ }
+
+ return 0;
+}
+void SampleTable::buildSampleEntriesTable() {
Mutex::Autolock autoLock(mLock);
- uint32_t cur_sample = 0;
- uint32_t time = 0;
+ if (mSampleTimeEntries != NULL) {
+ return;
+ }
+
+ mSampleTimeEntries = new SampleTimeEntry[mNumSampleSizes];
+
+ uint32_t sampleIndex = 0;
+ uint32_t sampleTime = 0;
+
for (uint32_t i = 0; i < mTimeToSampleCount; ++i) {
uint32_t n = mTimeToSample[2 * i];
uint32_t delta = mTimeToSample[2 * i + 1];
- if (req_time < time + n * delta) {
- int j = (req_time - time) / delta;
-
- uint32_t time1 = time + j * delta;
- uint32_t time2 = time1 + delta;
-
- uint32_t sampleTime;
- if (i+1 == mTimeToSampleCount
- || (abs_difference(req_time, time1)
- < abs_difference(req_time, time2))) {
- *sample_index = cur_sample + j;
- sampleTime = time1;
- } else {
- *sample_index = cur_sample + j + 1;
- sampleTime = time2;
- }
+ for (uint32_t j = 0; j < n; ++j) {
+ CHECK(sampleIndex < mNumSampleSizes);
- switch (flags) {
- case kFlagBefore:
- {
- if (sampleTime > req_time && *sample_index > 0) {
- --*sample_index;
- }
- break;
- }
+ mSampleTimeEntries[sampleIndex].mSampleIndex = sampleIndex;
- case kFlagAfter:
- {
- if (sampleTime < req_time
- && *sample_index + 1 < mNumSampleSizes) {
- ++*sample_index;
- }
- break;
- }
+ mSampleTimeEntries[sampleIndex].mCompositionTime =
+ sampleTime + getCompositionTimeOffset(sampleIndex);
+
+ ++sampleIndex;
+ sampleTime += delta;
+ }
+ }
+
+ qsort(mSampleTimeEntries, mNumSampleSizes, sizeof(SampleTimeEntry),
+ CompareIncreasingTime);
+}
- default:
- break;
+status_t SampleTable::findSampleAtTime(
+ uint32_t req_time, uint32_t *sample_index, uint32_t flags) {
+ buildSampleEntriesTable();
+
+ uint32_t left = 0;
+ uint32_t right = mNumSampleSizes;
+ while (left < right) {
+ uint32_t center = (left + right) / 2;
+ uint32_t centerTime = mSampleTimeEntries[center].mCompositionTime;
+
+ if (req_time < centerTime) {
+ right = center;
+ } else if (req_time > centerTime) {
+ left = center + 1;
+ } else {
+ left = center;
+ break;
+ }
+ }
+
+ if (left == mNumSampleSizes) {
+ --left;
+ }
+
+ uint32_t closestIndex = left;
+
+ switch (flags) {
+ case kFlagBefore:
+ {
+ while (closestIndex > 0
+ && mSampleTimeEntries[closestIndex].mCompositionTime
+ > req_time) {
+ --closestIndex;
}
+ break;
+ }
- return OK;
+ case kFlagAfter:
+ {
+ while (closestIndex + 1 < mNumSampleSizes
+ && mSampleTimeEntries[closestIndex].mCompositionTime
+ < req_time) {
+ ++closestIndex;
+ }
+ break;
}
- time += delta * n;
- cur_sample += n;
+ default:
+ {
+ CHECK(flags == kFlagClosest);
+
+ if (closestIndex > 0) {
+ // Check left neighbour and pick closest.
+ uint32_t absdiff1 =
+ abs_difference(
+ mSampleTimeEntries[closestIndex].mCompositionTime,
+ req_time);
+
+ uint32_t absdiff2 =
+ abs_difference(
+ mSampleTimeEntries[closestIndex - 1].mCompositionTime,
+ req_time);
+
+ if (absdiff1 > absdiff2) {
+ closestIndex = closestIndex - 1;
+ }
+ }
+
+ break;
+ }
}
- return ERROR_OUT_OF_RANGE;
+ *sample_index = mSampleTimeEntries[closestIndex].mSampleIndex;
+
+ return OK;
}
status_t SampleTable::findSyncSampleNear(
@@ -613,7 +678,7 @@ status_t SampleTable::getMetaDataForSample(
uint32_t sampleIndex,
off64_t *offset,
size_t *size,
- uint32_t *decodingTime,
+ uint32_t *compositionTime,
bool *isSyncSample) {
Mutex::Autolock autoLock(mLock);
@@ -630,8 +695,8 @@ status_t SampleTable::getMetaDataForSample(
*size = mSampleIterator->getSampleSize();
}
- if (decodingTime) {
- *decodingTime = mSampleIterator->getSampleTime();
+ if (compositionTime) {
+ *compositionTime = mSampleIterator->getSampleTime();
}
if (isSyncSample) {
diff --git a/media/libstagefright/include/MPEG4Extractor.h b/media/libstagefright/include/MPEG4Extractor.h
index d9ef208..3bd4c7e 100644
--- a/media/libstagefright/include/MPEG4Extractor.h
+++ b/media/libstagefright/include/MPEG4Extractor.h
@@ -92,8 +92,6 @@ private:
Track *findTrackByMimePrefix(const char *mimePrefix);
- status_t verifyIfStreamable();
-
MPEG4Extractor(const MPEG4Extractor &);
MPEG4Extractor &operator=(const MPEG4Extractor &);
};
diff --git a/media/libstagefright/include/NuCachedSource2.h b/media/libstagefright/include/NuCachedSource2.h
index 2128682..ed3e265c 100644
--- a/media/libstagefright/include/NuCachedSource2.h
+++ b/media/libstagefright/include/NuCachedSource2.h
@@ -96,7 +96,9 @@ private:
status_t seekInternal_l(off64_t offset);
size_t approxDataRemaining_l(status_t *finalStatus);
- void restartPrefetcherIfNecessary_l(bool ignoreLowWaterThreshold = false);
+
+ void restartPrefetcherIfNecessary_l(
+ bool ignoreLowWaterThreshold = false, bool force = false);
DISALLOW_EVIL_CONSTRUCTORS(NuCachedSource2);
};
diff --git a/media/libstagefright/include/SampleTable.h b/media/libstagefright/include/SampleTable.h
index 2f95de9..f44e0a2 100644
--- a/media/libstagefright/include/SampleTable.h
+++ b/media/libstagefright/include/SampleTable.h
@@ -63,7 +63,7 @@ public:
uint32_t sampleIndex,
off64_t *offset,
size_t *size,
- uint32_t *decodingTime,
+ uint32_t *compositionTime,
bool *isSyncSample = NULL);
enum {
@@ -107,6 +107,12 @@ private:
uint32_t mTimeToSampleCount;
uint32_t *mTimeToSample;
+ struct SampleTimeEntry {
+ uint32_t mSampleIndex;
+ uint32_t mCompositionTime;
+ };
+ SampleTimeEntry *mSampleTimeEntries;
+
uint32_t *mCompositionTimeDeltaEntries;
size_t mNumCompositionTimeDeltaEntries;
@@ -130,6 +136,10 @@ private:
uint32_t getCompositionTimeOffset(uint32_t sampleIndex) const;
+ static int CompareIncreasingTime(const void *, const void *);
+
+ void buildSampleEntriesTable();
+
SampleTable(const SampleTable &);
SampleTable &operator=(const SampleTable &);
};
diff --git a/opengl/libs/GLES2_dbg/Android.mk b/opengl/libs/GLES2_dbg/Android.mk
index 853cce6..9f6e68c 100644
--- a/opengl/libs/GLES2_dbg/Android.mk
+++ b/opengl/libs/GLES2_dbg/Android.mk
@@ -45,3 +45,5 @@ LOCAL_MODULE:= libGLESv2_dbg
LOCAL_MODULE_TAGS := optional
include $(BUILD_SHARED_LIBRARY)
+
+include $(LOCAL_PATH)/test/Android.mk
diff --git a/opengl/libs/GLES2_dbg/src/dbgcontext.cpp b/opengl/libs/GLES2_dbg/src/dbgcontext.cpp
index fe93874..71892d3 100644
--- a/opengl/libs/GLES2_dbg/src/dbgcontext.cpp
+++ b/opengl/libs/GLES2_dbg/src/dbgcontext.cpp
@@ -25,11 +25,11 @@ extern "C"
namespace android
{
-static pthread_key_t sEGLThreadLocalStorageKey = -1;
+pthread_key_t dbgEGLThreadLocalStorageKey = -1;
DbgContext * getDbgContextThreadSpecific()
{
- tls_t* tls = (tls_t*)pthread_getspecific(sEGLThreadLocalStorageKey);
+ tls_t* tls = (tls_t*)pthread_getspecific(dbgEGLThreadLocalStorageKey);
return tls->dbg;
}
@@ -63,7 +63,7 @@ DbgContext::~DbgContext()
DbgContext * CreateDbgContext(const pthread_key_t EGLThreadLocalStorageKey,
const unsigned version, const gl_hooks_t * const hooks)
{
- sEGLThreadLocalStorageKey = EGLThreadLocalStorageKey;
+ dbgEGLThreadLocalStorageKey = EGLThreadLocalStorageKey;
assert(version < 2);
assert(GL_NO_ERROR == hooks->gl.glGetError());
GLint MAX_VERTEX_ATTRIBS = 0;
@@ -149,6 +149,37 @@ void DbgContext::Compress(const void * in_data, unsigned int in_len,
}
}
+unsigned char * DbgContext::Decompress(const void * in, const unsigned int inLen,
+ unsigned int * const outLen)
+{
+ assert(inLen > 4 * 3);
+ if (inLen < 4 * 3)
+ return NULL;
+ *outLen = *(uint32_t *)in;
+ unsigned char * const out = (unsigned char *)malloc(*outLen);
+ unsigned int outPos = 0;
+ const unsigned char * const end = (const unsigned char *)in + inLen;
+ for (const unsigned char * inData = (const unsigned char *)in + 4; inData < end; ) {
+ const uint32_t chunkOut = *(uint32_t *)inData;
+ inData += 4;
+ const uint32_t chunkIn = *(uint32_t *)inData;
+ inData += 4;
+ if (chunkIn > 0) {
+ assert(inData + chunkIn <= end);
+ assert(outPos + chunkOut <= *outLen);
+ outPos += lzf_decompress(inData, chunkIn, out + outPos, chunkOut);
+ inData += chunkIn;
+ } else {
+ assert(inData + chunkOut <= end);
+ assert(outPos + chunkOut <= *outLen);
+ memcpy(out + outPos, inData, chunkOut);
+ inData += chunkOut;
+ outPos += chunkOut;
+ }
+ }
+ return out;
+}
+
void * DbgContext::GetReadPixelsBuffer(const unsigned size)
{
if (lzf_refBufSize < size + 8) {
diff --git a/opengl/libs/GLES2_dbg/src/header.h b/opengl/libs/GLES2_dbg/src/header.h
index c9e6c41..f2b1fa6 100644
--- a/opengl/libs/GLES2_dbg/src/header.h
+++ b/opengl/libs/GLES2_dbg/src/header.h
@@ -73,8 +73,9 @@ struct GLFunctionBitfield {
};
struct DbgContext {
-private:
static const unsigned int LZF_CHUNK_SIZE = 256 * 1024;
+
+private:
char * lzf_buf; // malloc / free; for lzf chunk compression and other uses
// used as buffer and reference frame for ReadPixels; malloc/free
@@ -129,6 +130,8 @@ public:
void Fetch(const unsigned index, std::string * const data) const;
void Compress(const void * in_data, unsigned in_len, std::string * const outStr);
+ static unsigned char * Decompress(const void * in, const unsigned int inLen,
+ unsigned int * const outLen); // malloc/free
void * GetReadPixelsBuffer(const unsigned size);
bool IsReadPixelBuffer(const void * const ptr) {
return ptr == lzf_ref[lzf_readIndex];
diff --git a/opengl/libs/GLES2_dbg/src/server.cpp b/opengl/libs/GLES2_dbg/src/server.cpp
index f13d6cc..ba4960d 100644
--- a/opengl/libs/GLES2_dbg/src/server.cpp
+++ b/opengl/libs/GLES2_dbg/src/server.cpp
@@ -186,7 +186,7 @@ float Send(const glesv2debugger::Message & msg, glesv2debugger::Message & cmd)
Die("Failed to send message length");
}
nsecs_t c0 = systemTime(timeMode);
- sent = send(clientSock, str.c_str(), str.length(), 0);
+ sent = send(clientSock, str.data(), str.length(), 0);
float t = (float)ns2ms(systemTime(timeMode) - c0);
if (sent != str.length()) {
LOGD("actual sent=%d expected=%d clientSock=%d", sent, str.length(), clientSock);
@@ -246,8 +246,9 @@ int * MessageLoop(FunctionCall & functionCall, glesv2debugger::Message & msg,
msg.set_function(function);
// when not exectResponse, set cmd to CONTINUE then SKIP
+ // cmd will be overwritten by received command
cmd.set_function(glesv2debugger::Message_Function_CONTINUE);
- cmd.set_expect_response(false);
+ cmd.set_expect_response(expectResponse);
glesv2debugger::Message_Function oldCmd = cmd.function();
Send(msg, cmd);
expectResponse = cmd.expect_response();
diff --git a/opengl/libs/GLES2_dbg/src/vertex.cpp b/opengl/libs/GLES2_dbg/src/vertex.cpp
index 7edc050..029ee3b 100644
--- a/opengl/libs/GLES2_dbg/src/vertex.cpp
+++ b/opengl/libs/GLES2_dbg/src/vertex.cpp
@@ -43,10 +43,8 @@ void Debug_glDrawArrays(GLenum mode, GLint first, GLsizei count)
void * pixels = NULL;
int viewport[4] = {};
- if (!expectResponse) {
- cmd.set_function(glesv2debugger::Message_Function_CONTINUE);
- cmd.set_expect_response(false);
- }
+ cmd.set_function(glesv2debugger::Message_Function_CONTINUE);
+ cmd.set_expect_response(expectResponse);
glesv2debugger::Message_Function oldCmd = cmd.function();
Send(msg, cmd);
expectResponse = cmd.expect_response();
@@ -61,8 +59,6 @@ void Debug_glDrawArrays(GLenum mode, GLint first, GLsizei count)
msg.set_function(glesv2debugger::Message_Function_glDrawArrays);
msg.set_type(glesv2debugger::Message_Type_AfterCall);
msg.set_expect_response(expectResponse);
- if (!expectResponse)
- cmd.set_function(glesv2debugger::Message_Function_SKIP);
if (!expectResponse) {
cmd.set_function(glesv2debugger::Message_Function_SKIP);
cmd.set_expect_response(false);
@@ -154,10 +150,8 @@ void Debug_glDrawElements(GLenum mode, GLsizei count, GLenum type, const GLvoid*
void * pixels = NULL;
int viewport[4] = {};
- if (!expectResponse) {
- cmd.set_function(glesv2debugger::Message_Function_CONTINUE);
- cmd.set_expect_response(false);
- }
+ cmd.set_function(glesv2debugger::Message_Function_CONTINUE);
+ cmd.set_expect_response(expectResponse);
glesv2debugger::Message_Function oldCmd = cmd.function();
Send(msg, cmd);
expectResponse = cmd.expect_response();
@@ -172,8 +166,6 @@ void Debug_glDrawElements(GLenum mode, GLsizei count, GLenum type, const GLvoid*
msg.set_function(glesv2debugger::Message_Function_glDrawElements);
msg.set_type(glesv2debugger::Message_Type_AfterCall);
msg.set_expect_response(expectResponse);
- if (!expectResponse)
- cmd.set_function(glesv2debugger::Message_Function_SKIP);
if (!expectResponse) {
cmd.set_function(glesv2debugger::Message_Function_SKIP);
cmd.set_expect_response(false);
diff --git a/opengl/libs/GLES2_dbg/test/Android.mk b/opengl/libs/GLES2_dbg/test/Android.mk
new file mode 100644
index 0000000..14a84b4
--- /dev/null
+++ b/opengl/libs/GLES2_dbg/test/Android.mk
@@ -0,0 +1,39 @@
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_C_INCLUDES := \
+ $(LOCAL_PATH) \
+ $(LOCAL_PATH)/../src \
+ $(LOCAL_PATH)/../../ \
+ external/gtest/include \
+ external/stlport/stlport \
+ external/protobuf/src \
+ bionic \
+ external \
+#
+
+LOCAL_SRC_FILES:= \
+ test_main.cpp \
+ test_server.cpp \
+ test_socket.cpp \
+#
+
+LOCAL_SHARED_LIBRARIES := libcutils libutils libGLESv2_dbg libstlport
+LOCAL_STATIC_LIBRARIES := libgtest libprotobuf-cpp-2.3.0-lite liblzf
+LOCAL_MODULE_TAGS := tests
+LOCAL_MODULE:= libGLESv2_dbg_test
+
+ifeq ($(ARCH_ARM_HAVE_TLS_REGISTER),true)
+ LOCAL_CFLAGS += -DHAVE_ARM_TLS_REGISTER
+endif
+ifneq ($(TARGET_SIMULATOR),true)
+ LOCAL_C_INCLUDES += bionic/libc/private
+endif
+
+LOCAL_CFLAGS += -DLOG_TAG=\"libEGL\"
+LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES
+LOCAL_CFLAGS += -fvisibility=hidden
+
+include $(BUILD_EXECUTABLE)
+
diff --git a/opengl/libs/GLES2_dbg/test/test_main.cpp b/opengl/libs/GLES2_dbg/test/test_main.cpp
new file mode 100644
index 0000000..058bea4
--- /dev/null
+++ b/opengl/libs/GLES2_dbg/test/test_main.cpp
@@ -0,0 +1,234 @@
+/*
+ ** Copyright 2011, The Android Open Source Project
+ **
+ ** Licensed under the Apache License, Version 2.0 (the "License");
+ ** you may not use this file except in compliance with the License.
+ ** You may obtain a copy of the License at
+ **
+ ** http://www.apache.org/licenses/LICENSE-2.0
+ **
+ ** Unless required by applicable law or agreed to in writing, software
+ ** distributed under the License is distributed on an "AS IS" BASIS,
+ ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ** See the License for the specific language governing permissions and
+ ** limitations under the License.
+ */
+
+#include "header.h"
+#include "gtest/gtest.h"
+#include "hooks.h"
+
+namespace
+{
+
+// The fixture for testing class Foo.
+class DbgContextTest : public ::testing::Test
+{
+protected:
+ android::DbgContext dbg;
+ gl_hooks_t hooks;
+
+ DbgContextTest()
+ : dbg(1, &hooks, 32, GL_RGBA, GL_UNSIGNED_BYTE) {
+ // You can do set-up work for each test here.
+ hooks.gl.glGetError = GetError;
+ }
+
+ static GLenum GetError() {
+ return GL_NO_ERROR;
+ }
+
+ virtual ~DbgContextTest() {
+ // You can do clean-up work that doesn't throw exceptions here.
+ }
+
+ // If the constructor and destructor are not enough for setting up
+ // and cleaning up each test, you can define the following methods:
+
+ virtual void SetUp() {
+ // Code here will be called immediately after the constructor (right
+ // before each test).
+ }
+
+ virtual void TearDown() {
+ // Code here will be called immediately after each test (right
+ // before the destructor).
+ }
+};
+
+TEST_F(DbgContextTest, GetReadPixelBuffer)
+{
+ const unsigned int bufferSize = 512;
+ // test that it's allocating two buffers and swapping them
+ void * const buffer0 = dbg.GetReadPixelsBuffer(bufferSize);
+ ASSERT_NE((void *)NULL, buffer0);
+ for (unsigned int i = 0; i < bufferSize / sizeof(unsigned int); i++) {
+ EXPECT_EQ(0, ((unsigned int *)buffer0)[i])
+ << "GetReadPixelsBuffer should allocate and zero";
+ ((unsigned int *)buffer0)[i] = i * 13;
+ }
+
+ void * const buffer1 = dbg.GetReadPixelsBuffer(bufferSize);
+ ASSERT_NE((void *)NULL, buffer1);
+ EXPECT_NE(buffer0, buffer1);
+ for (unsigned int i = 0; i < bufferSize / sizeof(unsigned int); i++) {
+ EXPECT_EQ(0, ((unsigned int *)buffer1)[i])
+ << "GetReadPixelsBuffer should allocate and zero";
+ ((unsigned int *)buffer1)[i] = i * 17;
+ }
+
+ void * const buffer2 = dbg.GetReadPixelsBuffer(bufferSize);
+ EXPECT_EQ(buffer2, buffer0);
+ for (unsigned int i = 0; i < bufferSize / sizeof(unsigned int); i++)
+ EXPECT_EQ(i * 13, ((unsigned int *)buffer2)[i])
+ << "GetReadPixelsBuffer should swap buffers";
+
+ void * const buffer3 = dbg.GetReadPixelsBuffer(bufferSize);
+ EXPECT_EQ(buffer3, buffer1);
+ for (unsigned int i = 0; i < bufferSize / sizeof(unsigned int); i++)
+ EXPECT_EQ(i * 17, ((unsigned int *)buffer3)[i])
+ << "GetReadPixelsBuffer should swap buffers";
+
+ void * const buffer4 = dbg.GetReadPixelsBuffer(bufferSize);
+ EXPECT_NE(buffer3, buffer4);
+ EXPECT_EQ(buffer0, buffer2);
+ EXPECT_EQ(buffer1, buffer3);
+ EXPECT_EQ(buffer2, buffer4);
+
+ // it reallocs as necessary; 0 size may result in NULL
+ for (unsigned int i = 0; i < 42; i++) {
+ void * const buffer = dbg.GetReadPixelsBuffer(((i & 7)) << 20);
+ EXPECT_NE((void *)NULL, buffer)
+ << "should be able to get a variety of reasonable sizes";
+ EXPECT_TRUE(dbg.IsReadPixelBuffer(buffer));
+ }
+}
+
+TEST_F(DbgContextTest, CompressReadPixelBuffer)
+{
+ const unsigned int bufferSize = dbg.LZF_CHUNK_SIZE * 4 + 33;
+ std::string out;
+ unsigned char * buffer = (unsigned char *)dbg.GetReadPixelsBuffer(bufferSize);
+ for (unsigned int i = 0; i < bufferSize; i++)
+ buffer[i] = i * 13;
+ dbg.CompressReadPixelBuffer(&out);
+ uint32_t decompSize = 0;
+ ASSERT_LT(12, out.length()); // at least written chunk header
+ ASSERT_EQ(bufferSize, *(uint32_t *)out.data())
+ << "total decompressed size should be as requested in GetReadPixelsBuffer";
+ for (unsigned int i = 4; i < out.length();) {
+ const uint32_t outSize = *(uint32_t *)(out.data() + i);
+ i += 4;
+ const uint32_t inSize = *(uint32_t *)(out.data() + i);
+ i += 4;
+ if (inSize == 0)
+ i += outSize; // chunk not compressed
+ else
+ i += inSize; // skip the actual compressed chunk
+ decompSize += outSize;
+ }
+ ASSERT_EQ(bufferSize, decompSize);
+ decompSize = 0;
+
+ unsigned char * decomp = dbg.Decompress(out.data(), out.length(), &decompSize);
+ ASSERT_EQ(decompSize, bufferSize);
+ for (unsigned int i = 0; i < bufferSize; i++)
+ EXPECT_EQ((unsigned char)(i * 13), decomp[i]) << "xor with 0 ref is identity";
+ free(decomp);
+
+ buffer = (unsigned char *)dbg.GetReadPixelsBuffer(bufferSize);
+ for (unsigned int i = 0; i < bufferSize; i++)
+ buffer[i] = i * 13;
+ out.clear();
+ dbg.CompressReadPixelBuffer(&out);
+ decompSize = 0;
+ decomp = dbg.Decompress(out.data(), out.length(), &decompSize);
+ ASSERT_EQ(decompSize, bufferSize);
+ for (unsigned int i = 0; i < bufferSize; i++)
+ EXPECT_EQ(0, decomp[i]) << "xor with same ref is 0";
+ free(decomp);
+
+ buffer = (unsigned char *)dbg.GetReadPixelsBuffer(bufferSize);
+ for (unsigned int i = 0; i < bufferSize; i++)
+ buffer[i] = i * 19;
+ out.clear();
+ dbg.CompressReadPixelBuffer(&out);
+ decompSize = 0;
+ decomp = dbg.Decompress(out.data(), out.length(), &decompSize);
+ ASSERT_EQ(decompSize, bufferSize);
+ for (unsigned int i = 0; i < bufferSize; i++)
+ EXPECT_EQ((unsigned char)(i * 13) ^ (unsigned char)(i * 19), decomp[i])
+ << "xor ref";
+ free(decomp);
+}
+
+TEST_F(DbgContextTest, UseProgram)
+{
+ static const GLuint _program = 74568;
+ static const struct Attribute {
+ const char * name;
+ GLint location;
+ GLint size;
+ GLenum type;
+ } _attributes [] = {
+ {"aaa", 2, 2, GL_FLOAT_VEC2},
+ {"bb", 6, 2, GL_FLOAT_MAT2},
+ {"c", 1, 1, GL_FLOAT},
+ };
+ static const unsigned int _attributeCount = sizeof(_attributes) / sizeof(*_attributes);
+ struct GL {
+ static void GetProgramiv(GLuint program, GLenum pname, GLint* params) {
+ EXPECT_EQ(_program, program);
+ ASSERT_NE((GLint *)NULL, params);
+ switch (pname) {
+ case GL_ACTIVE_ATTRIBUTES:
+ *params = _attributeCount;
+ return;
+ case GL_ACTIVE_ATTRIBUTE_MAX_LENGTH:
+ *params = 4; // includes NULL terminator
+ return;
+ default:
+ ADD_FAILURE() << "not handled pname: " << pname;
+ }
+ }
+
+ static GLint GetAttribLocation(GLuint program, const GLchar* name) {
+ EXPECT_EQ(_program, program);
+ for (unsigned int i = 0; i < _attributeCount; i++)
+ if (!strcmp(name, _attributes[i].name))
+ return _attributes[i].location;
+ ADD_FAILURE() << "unknown attribute name: " << name;
+ return -1;
+ }
+
+ static void GetActiveAttrib(GLuint program, GLuint index, GLsizei bufsize,
+ GLsizei* length, GLint* size, GLenum* type, GLchar* name) {
+ EXPECT_EQ(_program, program);
+ ASSERT_LT(index, _attributeCount);
+ const Attribute & att = _attributes[index];
+ ASSERT_GE(bufsize, strlen(att.name) + 1);
+ ASSERT_NE((GLint *)NULL, size);
+ ASSERT_NE((GLenum *)NULL, type);
+ ASSERT_NE((GLchar *)NULL, name);
+ strcpy(name, att.name);
+ if (length)
+ *length = strlen(name) + 1;
+ *size = att.size;
+ *type = att.type;
+ }
+ };
+ hooks.gl.glGetProgramiv = GL::GetProgramiv;
+ hooks.gl.glGetAttribLocation = GL::GetAttribLocation;
+ hooks.gl.glGetActiveAttrib = GL::GetActiveAttrib;
+ dbg.glUseProgram(_program);
+ EXPECT_EQ(10, dbg.maxAttrib);
+ dbg.glUseProgram(0);
+ EXPECT_EQ(0, dbg.maxAttrib);
+}
+} // namespace
+
+int main(int argc, char **argv)
+{
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/opengl/libs/GLES2_dbg/test/test_server.cpp b/opengl/libs/GLES2_dbg/test/test_server.cpp
new file mode 100644
index 0000000..7fb87ea
--- /dev/null
+++ b/opengl/libs/GLES2_dbg/test/test_server.cpp
@@ -0,0 +1,197 @@
+/*
+ ** Copyright 2011, The Android Open Source Project
+ **
+ ** Licensed under the Apache License, Version 2.0 (the "License");
+ ** you may not use this file except in compliance with the License.
+ ** You may obtain a copy of the License at
+ **
+ ** http://www.apache.org/licenses/LICENSE-2.0
+ **
+ ** Unless required by applicable law or agreed to in writing, software
+ ** distributed under the License is distributed on an "AS IS" BASIS,
+ ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ** See the License for the specific language governing permissions and
+ ** limitations under the License.
+ */
+
+#include "header.h"
+#include "gtest/gtest.h"
+#include "egl_tls.h"
+#include "hooks.h"
+
+namespace android
+{
+extern FILE * file;
+extern unsigned int MAX_FILE_SIZE;
+extern pthread_key_t dbgEGLThreadLocalStorageKey;
+};
+
+// tmpfile fails, so need to manually make a writable file first
+static const char * filePath = "/data/local/tmp/dump.gles2dbg";
+
+class ServerFileTest : public ::testing::Test
+{
+protected:
+ ServerFileTest() { }
+
+ virtual ~ServerFileTest() { }
+
+ virtual void SetUp() {
+ MAX_FILE_SIZE = 8 << 20;
+ ASSERT_EQ((FILE *)NULL, file);
+ file = fopen("/data/local/tmp/dump.gles2dbg", "wb+");
+ ASSERT_NE((FILE *)NULL, file) << "make sure file is writable: "
+ << filePath;
+ }
+
+ virtual void TearDown() {
+ ASSERT_NE((FILE *)NULL, file);
+ fclose(file);
+ file = NULL;
+ }
+
+ void Read(glesv2debugger::Message & msg) const {
+ msg.Clear();
+ uint32_t len = 0;
+ ASSERT_EQ(sizeof(len), fread(&len, 1, sizeof(len), file));
+ ASSERT_GT(len, 0u);
+ char * buffer = new char [len];
+ ASSERT_EQ(len, fread(buffer, 1, len, file));
+ msg.ParseFromArray(buffer, len);
+ delete buffer;
+ }
+};
+
+
+
+TEST_F(ServerFileTest, Send)
+{
+ glesv2debugger::Message msg, cmd, read;
+ msg.set_context_id(1);
+ msg.set_function(msg.glFinish);
+ msg.set_expect_response(false);
+ msg.set_type(msg.BeforeCall);
+ rewind(file);
+ android::Send(msg, cmd);
+ rewind(file);
+ Read(read);
+ EXPECT_EQ(msg.context_id(), read.context_id());
+ EXPECT_EQ(msg.function(), read.function());
+ EXPECT_EQ(msg.expect_response(), read.expect_response());
+ EXPECT_EQ(msg.type(), read.type());
+}
+
+void * glNoop()
+{
+ return 0;
+}
+
+class ServerFileContextTest : public ServerFileTest
+{
+protected:
+ tls_t tls;
+ gl_hooks_t hooks;
+
+ ServerFileContextTest() { }
+
+ virtual ~ServerFileContextTest() { }
+
+ virtual void SetUp() {
+ ServerFileTest::SetUp();
+
+ if (dbgEGLThreadLocalStorageKey == -1)
+ pthread_key_create(&dbgEGLThreadLocalStorageKey, NULL);
+ ASSERT_NE(-1, dbgEGLThreadLocalStorageKey);
+ tls.dbg = new DbgContext(1, &hooks, 32, GL_RGBA, GL_UNSIGNED_BYTE);
+ ASSERT_NE((void *)NULL, tls.dbg);
+ pthread_setspecific(dbgEGLThreadLocalStorageKey, &tls);
+ for (unsigned int i = 0; i < sizeof(hooks) / sizeof(void *); i++)
+ ((void **)&hooks)[i] = reinterpret_cast<void *>(glNoop);
+ }
+
+ virtual void TearDown() {
+ ServerFileTest::TearDown();
+ }
+};
+
+TEST_F(ServerFileContextTest, MessageLoop)
+{
+ static const int arg0 = 45;
+ static const float arg7 = -87.2331f;
+ static const int arg8 = -3;
+ static const int * ret = reinterpret_cast<int *>(870);
+
+ struct Caller : public FunctionCall {
+ const int * operator()(gl_hooks_t::gl_t const * const _c, glesv2debugger::Message & msg) {
+ msg.set_arg0(arg0);
+ msg.set_arg7((int &)arg7);
+ msg.set_arg8(arg8);
+ return ret;
+ }
+ } caller;
+ const int contextId = reinterpret_cast<int>(tls.dbg);
+ glesv2debugger::Message msg, read;
+
+ EXPECT_EQ(ret, MessageLoop(caller, msg, msg.glFinish));
+
+ rewind(file);
+ Read(read);
+ EXPECT_EQ(contextId, read.context_id());
+ EXPECT_EQ(read.glFinish, read.function());
+ EXPECT_EQ(false, read.expect_response());
+ EXPECT_EQ(read.BeforeCall, read.type());
+
+ Read(read);
+ EXPECT_EQ(contextId, read.context_id());
+ EXPECT_EQ(read.glFinish, read.function());
+ EXPECT_EQ(false, read.expect_response());
+ EXPECT_EQ(read.AfterCall, read.type());
+ EXPECT_TRUE(read.has_time());
+ EXPECT_EQ(arg0, read.arg0());
+ const int readArg7 = read.arg7();
+ EXPECT_EQ(arg7, (float &)readArg7);
+ EXPECT_EQ(arg8, read.arg8());
+
+ const long pos = ftell(file);
+ fseek(file, 0, SEEK_END);
+ EXPECT_EQ(pos, ftell(file))
+ << "should only write the BeforeCall and AfterCall messages";
+}
+
+TEST_F(ServerFileContextTest, DisableEnableVertexAttribArray)
+{
+ Debug_glEnableVertexAttribArray(tls.dbg->MAX_VERTEX_ATTRIBS + 2); // should just ignore invalid index
+
+ glesv2debugger::Message read;
+ rewind(file);
+ Read(read);
+ EXPECT_EQ(read.glEnableVertexAttribArray, read.function());
+ EXPECT_EQ(tls.dbg->MAX_VERTEX_ATTRIBS + 2, read.arg0());
+ Read(read);
+
+ rewind(file);
+ Debug_glDisableVertexAttribArray(tls.dbg->MAX_VERTEX_ATTRIBS + 4); // should just ignore invalid index
+ rewind(file);
+ Read(read);
+ Read(read);
+
+ for (unsigned int i = 0; i < tls.dbg->MAX_VERTEX_ATTRIBS; i += 5) {
+ rewind(file);
+ Debug_glEnableVertexAttribArray(i);
+ EXPECT_TRUE(tls.dbg->vertexAttribs[i].enabled);
+ rewind(file);
+ Read(read);
+ EXPECT_EQ(read.glEnableVertexAttribArray, read.function());
+ EXPECT_EQ(i, read.arg0());
+ Read(read);
+
+ rewind(file);
+ Debug_glDisableVertexAttribArray(i);
+ EXPECT_FALSE(tls.dbg->vertexAttribs[i].enabled);
+ rewind(file);
+ Read(read);
+ EXPECT_EQ(read.glDisableVertexAttribArray, read.function());
+ EXPECT_EQ(i, read.arg0());
+ Read(read);
+ }
+}
diff --git a/opengl/libs/GLES2_dbg/test/test_socket.cpp b/opengl/libs/GLES2_dbg/test/test_socket.cpp
new file mode 100644
index 0000000..1c31a94
--- /dev/null
+++ b/opengl/libs/GLES2_dbg/test/test_socket.cpp
@@ -0,0 +1,474 @@
+/*
+ ** Copyright 2011, The Android Open Source Project
+ **
+ ** Licensed under the Apache License, Version 2.0 (the "License");
+ ** you may not use this file except in compliance with the License.
+ ** You may obtain a copy of the License at
+ **
+ ** http://www.apache.org/licenses/LICENSE-2.0
+ **
+ ** Unless required by applicable law or agreed to in writing, software
+ ** distributed under the License is distributed on an "AS IS" BASIS,
+ ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ** See the License for the specific language governing permissions and
+ ** limitations under the License.
+ */
+
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+
+#include "header.h"
+#include "gtest/gtest.h"
+#include "egl_tls.h"
+#include "hooks.h"
+
+namespace android
+{
+extern int serverSock, clientSock;
+extern pthread_key_t dbgEGLThreadLocalStorageKey;
+};
+
+void * glNoop();
+
+class SocketContextTest : public ::testing::Test
+{
+protected:
+ tls_t tls;
+ gl_hooks_t hooks;
+ int sock;
+ char * buffer;
+ unsigned int bufferSize;
+
+ SocketContextTest() : sock(-1) {
+ }
+
+ virtual ~SocketContextTest() {
+ }
+
+ virtual void SetUp() {
+ if (dbgEGLThreadLocalStorageKey == -1)
+ pthread_key_create(&dbgEGLThreadLocalStorageKey, NULL);
+ ASSERT_NE(-1, dbgEGLThreadLocalStorageKey);
+ tls.dbg = new DbgContext(1, &hooks, 32, GL_RGBA, GL_UNSIGNED_BYTE);
+ ASSERT_NE((void *)NULL, tls.dbg);
+ pthread_setspecific(dbgEGLThreadLocalStorageKey, &tls);
+ for (unsigned int i = 0; i < sizeof(hooks) / sizeof(void *); i++)
+ ((void **)&hooks)[i] = (void *)glNoop;
+
+ int socks[2] = {-1, -1};
+ ASSERT_EQ(0, socketpair(AF_UNIX, SOCK_STREAM, 0, socks));
+ clientSock = socks[0];
+ sock = socks[1];
+
+ bufferSize = 128;
+ buffer = new char [128];
+ ASSERT_NE((char *)NULL, buffer);
+ }
+
+ virtual void TearDown() {
+ close(sock);
+ close(clientSock);
+ clientSock = -1;
+ delete buffer;
+ }
+
+ void Write(glesv2debugger::Message & msg) const {
+ msg.set_context_id((int)tls.dbg);
+ msg.set_type(msg.Response);
+ ASSERT_TRUE(msg.has_context_id());
+ ASSERT_TRUE(msg.has_function());
+ ASSERT_TRUE(msg.has_type());
+ ASSERT_TRUE(msg.has_expect_response());
+ static std::string str;
+ msg.SerializeToString(&str);
+ const uint32_t len = str.length();
+ ASSERT_EQ(sizeof(len), send(sock, &len, sizeof(len), 0));
+ ASSERT_EQ(str.length(), send(sock, str.data(), str.length(), 0));
+ }
+
+ void Read(glesv2debugger::Message & msg) {
+ int available = 0;
+ ASSERT_EQ(0, ioctl(sock, FIONREAD, &available));
+ ASSERT_GT(available, 0);
+ uint32_t len = 0;
+ ASSERT_EQ(sizeof(len), recv(sock, &len, sizeof(len), 0));
+ if (len > bufferSize) {
+ bufferSize = len;
+ buffer = new char[bufferSize];
+ ASSERT_NE((char *)NULL, buffer);
+ }
+ ASSERT_EQ(len, recv(sock, buffer, len, 0));
+ msg.Clear();
+ msg.ParseFromArray(buffer, len);
+ ASSERT_TRUE(msg.has_context_id());
+ ASSERT_TRUE(msg.has_function());
+ ASSERT_TRUE(msg.has_type());
+ ASSERT_TRUE(msg.has_expect_response());
+ }
+
+ void CheckNoAvailable() {
+ int available = 0;
+ ASSERT_EQ(0, ioctl(sock, FIONREAD, &available));
+ ASSERT_EQ(available, 0);
+ }
+};
+
+TEST_F(SocketContextTest, MessageLoopSkip)
+{
+ static const int arg0 = 45;
+ static const float arg7 = -87.2331f;
+ static const int arg8 = -3;
+ static const int * ret = (int *)870;
+
+ struct Caller : public FunctionCall {
+ const int * operator()(gl_hooks_t::gl_t const * const _c, glesv2debugger::Message & msg) {
+ msg.set_arg0(arg0);
+ msg.set_arg7((int &)arg7);
+ msg.set_arg8(arg8);
+ return ret;
+ }
+ } caller;
+ glesv2debugger::Message msg, read, cmd;
+ tls.dbg->expectResponse.Bit(msg.glFinish, true);
+
+ cmd.set_function(cmd.SKIP);
+ cmd.set_expect_response(false);
+ Write(cmd);
+
+ EXPECT_NE(ret, MessageLoop(caller, msg, msg.glFinish));
+
+ Read(read);
+ EXPECT_EQ(read.glFinish, read.function());
+ EXPECT_EQ(read.BeforeCall, read.type());
+ EXPECT_NE(arg0, read.arg0());
+ EXPECT_NE((int &)arg7, read.arg7());
+ EXPECT_NE(arg8, read.arg8());
+
+ CheckNoAvailable();
+}
+
+TEST_F(SocketContextTest, MessageLoopContinue)
+{
+ static const int arg0 = GL_FRAGMENT_SHADER;
+ static const int ret = -342;
+ struct Caller : public FunctionCall {
+ const int * operator()(gl_hooks_t::gl_t const * const _c, glesv2debugger::Message & msg) {
+ msg.set_ret(ret);
+ return (int *)ret;
+ }
+ } caller;
+ glesv2debugger::Message msg, read, cmd;
+ tls.dbg->expectResponse.Bit(msg.glCreateShader, true);
+
+ cmd.set_function(cmd.CONTINUE);
+ cmd.set_expect_response(false); // MessageLoop should automatically skip after continue
+ Write(cmd);
+
+ msg.set_arg0(arg0);
+ EXPECT_EQ((int *)ret, MessageLoop(caller, msg, msg.glCreateShader));
+
+ Read(read);
+ EXPECT_EQ(read.glCreateShader, read.function());
+ EXPECT_EQ(read.BeforeCall, read.type());
+ EXPECT_EQ(arg0, read.arg0());
+
+ Read(read);
+ EXPECT_EQ(read.glCreateShader, read.function());
+ EXPECT_EQ(read.AfterCall, read.type());
+ EXPECT_EQ(ret, read.ret());
+
+ CheckNoAvailable();
+}
+
+TEST_F(SocketContextTest, MessageLoopGenerateCall)
+{
+ static const int ret = -342;
+ static unsigned int createShader, createProgram;
+ createShader = 0;
+ createProgram = 0;
+ struct Caller : public FunctionCall {
+ const int * operator()(gl_hooks_t::gl_t const * const _c, glesv2debugger::Message & msg) {
+ const int r = (int)_c->glCreateProgram();
+ msg.set_ret(r);
+ return (int *)r;
+ }
+ static GLuint CreateShader(const GLenum type) {
+ createShader++;
+ return type;
+ }
+ static GLuint CreateProgram() {
+ createProgram++;
+ return ret;
+ }
+ } caller;
+ glesv2debugger::Message msg, read, cmd;
+ hooks.gl.glCreateShader = caller.CreateShader;
+ hooks.gl.glCreateProgram = caller.CreateProgram;
+ tls.dbg->expectResponse.Bit(msg.glCreateProgram, true);
+
+ cmd.set_function(cmd.glCreateShader);
+ cmd.set_arg0(GL_FRAGMENT_SHADER);
+ cmd.set_expect_response(true);
+ Write(cmd);
+
+ cmd.Clear();
+ cmd.set_function(cmd.CONTINUE);
+ cmd.set_expect_response(true);
+ Write(cmd);
+
+ cmd.set_function(cmd.glCreateShader);
+ cmd.set_arg0(GL_VERTEX_SHADER);
+ cmd.set_expect_response(false); // MessageLoop should automatically skip afterwards
+ Write(cmd);
+
+ EXPECT_EQ((int *)ret, MessageLoop(caller, msg, msg.glCreateProgram));
+
+ Read(read);
+ EXPECT_EQ(read.glCreateProgram, read.function());
+ EXPECT_EQ(read.BeforeCall, read.type());
+
+ Read(read);
+ EXPECT_EQ(read.glCreateShader, read.function());
+ EXPECT_EQ(read.AfterGeneratedCall, read.type());
+ EXPECT_EQ(GL_FRAGMENT_SHADER, read.ret());
+
+ Read(read);
+ EXPECT_EQ(read.glCreateProgram, read.function());
+ EXPECT_EQ(read.AfterCall, read.type());
+ EXPECT_EQ(ret, read.ret());
+
+ Read(read);
+ EXPECT_EQ(read.glCreateShader, read.function());
+ EXPECT_EQ(read.AfterGeneratedCall, read.type());
+ EXPECT_EQ(GL_VERTEX_SHADER, read.ret());
+
+ EXPECT_EQ(2, createShader);
+ EXPECT_EQ(1, createProgram);
+
+ CheckNoAvailable();
+}
+
+TEST_F(SocketContextTest, MessageLoopSetProp)
+{
+ static const int ret = -342;
+ static unsigned int createShader, createProgram;
+ createShader = 0;
+ createProgram = 0;
+ struct Caller : public FunctionCall {
+ const int * operator()(gl_hooks_t::gl_t const * const _c, glesv2debugger::Message & msg) {
+ const int r = (int)_c->glCreateProgram();
+ msg.set_ret(r);
+ return (int *)r;
+ }
+ static GLuint CreateShader(const GLenum type) {
+ createShader++;
+ return type;
+ }
+ static GLuint CreateProgram() {
+ createProgram++;
+ return ret;
+ }
+ } caller;
+ glesv2debugger::Message msg, read, cmd;
+ hooks.gl.glCreateShader = caller.CreateShader;
+ hooks.gl.glCreateProgram = caller.CreateProgram;
+ tls.dbg->expectResponse.Bit(msg.glCreateProgram, false);
+
+ cmd.set_function(cmd.SETPROP);
+ cmd.set_prop(cmd.ExpectResponse);
+ cmd.set_arg0(cmd.glCreateProgram);
+ cmd.set_arg1(true);
+ cmd.set_expect_response(true);
+ Write(cmd);
+
+ cmd.Clear();
+ cmd.set_function(cmd.glCreateShader);
+ cmd.set_arg0(GL_FRAGMENT_SHADER);
+ cmd.set_expect_response(true);
+ Write(cmd);
+
+ cmd.set_function(cmd.SETPROP);
+ cmd.set_prop(cmd.CaptureDraw);
+ cmd.set_arg0(819);
+ cmd.set_expect_response(true);
+ Write(cmd);
+
+ cmd.Clear();
+ cmd.set_function(cmd.CONTINUE);
+ cmd.set_expect_response(true);
+ Write(cmd);
+
+ cmd.set_function(cmd.glCreateShader);
+ cmd.set_arg0(GL_VERTEX_SHADER);
+ cmd.set_expect_response(false); // MessageLoop should automatically skip afterwards
+ Write(cmd);
+
+ EXPECT_EQ((int *)ret, MessageLoop(caller, msg, msg.glCreateProgram));
+
+ EXPECT_TRUE(tls.dbg->expectResponse.Bit(msg.glCreateProgram));
+ EXPECT_EQ(819, tls.dbg->captureDraw);
+
+ Read(read);
+ EXPECT_EQ(read.glCreateProgram, read.function());
+ EXPECT_EQ(read.BeforeCall, read.type());
+
+ Read(read);
+ EXPECT_EQ(read.glCreateShader, read.function());
+ EXPECT_EQ(read.AfterGeneratedCall, read.type());
+ EXPECT_EQ(GL_FRAGMENT_SHADER, read.ret());
+
+ Read(read);
+ EXPECT_EQ(read.glCreateProgram, read.function());
+ EXPECT_EQ(read.AfterCall, read.type());
+ EXPECT_EQ(ret, read.ret());
+
+ Read(read);
+ EXPECT_EQ(read.glCreateShader, read.function());
+ EXPECT_EQ(read.AfterGeneratedCall, read.type());
+ EXPECT_EQ(GL_VERTEX_SHADER, read.ret());
+
+ EXPECT_EQ(2, createShader);
+ EXPECT_EQ(1, createProgram);
+
+ CheckNoAvailable();
+}
+
+TEST_F(SocketContextTest, TexImage2D)
+{
+ static const GLenum _target = GL_TEXTURE_2D;
+ static const GLint _level = 1, _internalformat = GL_RGBA;
+ static const GLsizei _width = 2, _height = 2;
+ static const GLint _border = 333;
+ static const GLenum _format = GL_RGB, _type = GL_UNSIGNED_SHORT_5_6_5;
+ static const short _pixels [_width * _height] = {11, 22, 33, 44};
+ static unsigned int texImage2D;
+ texImage2D = 0;
+
+ struct Caller {
+ static void TexImage2D(GLenum target, GLint level, GLint internalformat,
+ GLsizei width, GLsizei height, GLint border,
+ GLenum format, GLenum type, const GLvoid* pixels) {
+ EXPECT_EQ(_target, target);
+ EXPECT_EQ(_level, level);
+ EXPECT_EQ(_internalformat, internalformat);
+ EXPECT_EQ(_width, width);
+ EXPECT_EQ(_height, height);
+ EXPECT_EQ(_border, border);
+ EXPECT_EQ(_format, format);
+ EXPECT_EQ(_type, type);
+ EXPECT_EQ(0, memcmp(_pixels, pixels, sizeof(_pixels)));
+ texImage2D++;
+ }
+ } caller;
+ glesv2debugger::Message msg, read, cmd;
+ hooks.gl.glTexImage2D = caller.TexImage2D;
+ tls.dbg->expectResponse.Bit(msg.glTexImage2D, false);
+
+ Debug_glTexImage2D(_target, _level, _internalformat, _width, _height, _border,
+ _format, _type, _pixels);
+ EXPECT_EQ(1, texImage2D);
+
+ Read(read);
+ EXPECT_EQ(read.glTexImage2D, read.function());
+ EXPECT_EQ(read.BeforeCall, read.type());
+ EXPECT_EQ(_target, read.arg0());
+ EXPECT_EQ(_level, read.arg1());
+ EXPECT_EQ(_internalformat, read.arg2());
+ EXPECT_EQ(_width, read.arg3());
+ EXPECT_EQ(_height, read.arg4());
+ EXPECT_EQ(_border, read.arg5());
+ EXPECT_EQ(_format, read.arg6());
+ EXPECT_EQ(_type, read.arg7());
+
+ EXPECT_TRUE(read.has_data());
+ uint32_t dataLen = 0;
+ const unsigned char * data = tls.dbg->Decompress(read.data().data(),
+ read.data().length(), &dataLen);
+ EXPECT_EQ(sizeof(_pixels), dataLen);
+ if (sizeof(_pixels) == dataLen)
+ EXPECT_EQ(0, memcmp(_pixels, data, sizeof(_pixels)));
+
+ Read(read);
+ EXPECT_EQ(read.glTexImage2D, read.function());
+ EXPECT_EQ(read.AfterCall, read.type());
+
+ CheckNoAvailable();
+}
+
+TEST_F(SocketContextTest, CopyTexImage2D)
+{
+ static const GLenum _target = GL_TEXTURE_2D;
+ static const GLint _level = 1, _internalformat = GL_RGBA;
+ static const GLint _x = 9, _y = 99;
+ static const GLsizei _width = 2, _height = 3;
+ static const GLint _border = 333;
+ static const int _pixels [_width * _height] = {11, 22, 33, 44, 55, 66};
+ static unsigned int copyTexImage2D, readPixels;
+ copyTexImage2D = 0, readPixels = 0;
+
+ struct Caller {
+ static void CopyTexImage2D(GLenum target, GLint level, GLenum internalformat,
+ GLint x, GLint y, GLsizei width, GLsizei height, GLint border) {
+ EXPECT_EQ(_target, target);
+ EXPECT_EQ(_level, level);
+ EXPECT_EQ(_internalformat, internalformat);
+ EXPECT_EQ(_x, x);
+ EXPECT_EQ(_y, y);
+ EXPECT_EQ(_width, width);
+ EXPECT_EQ(_height, height);
+ EXPECT_EQ(_border, border);
+ copyTexImage2D++;
+ }
+ static void ReadPixels(GLint x, GLint y, GLsizei width, GLsizei height,
+ GLenum format, GLenum type, GLvoid* pixels) {
+ EXPECT_EQ(_x, x);
+ EXPECT_EQ(_y, y);
+ EXPECT_EQ(_width, width);
+ EXPECT_EQ(_height, height);
+ EXPECT_EQ(GL_RGBA, format);
+ EXPECT_EQ(GL_UNSIGNED_BYTE, type);
+ ASSERT_NE((void *)NULL, pixels);
+ memcpy(pixels, _pixels, sizeof(_pixels));
+ readPixels++;
+ }
+ } caller;
+ glesv2debugger::Message msg, read, cmd;
+ hooks.gl.glCopyTexImage2D = caller.CopyTexImage2D;
+ hooks.gl.glReadPixels = caller.ReadPixels;
+ tls.dbg->expectResponse.Bit(msg.glCopyTexImage2D, false);
+
+ Debug_glCopyTexImage2D(_target, _level, _internalformat, _x, _y, _width, _height,
+ _border);
+ ASSERT_EQ(1, copyTexImage2D);
+ ASSERT_EQ(1, readPixels);
+
+ Read(read);
+ EXPECT_EQ(read.glCopyTexImage2D, read.function());
+ EXPECT_EQ(read.BeforeCall, read.type());
+ EXPECT_EQ(_target, read.arg0());
+ EXPECT_EQ(_level, read.arg1());
+ EXPECT_EQ(_internalformat, read.arg2());
+ EXPECT_EQ(_x, read.arg3());
+ EXPECT_EQ(_y, read.arg4());
+ EXPECT_EQ(_width, read.arg5());
+ EXPECT_EQ(_height, read.arg6());
+ EXPECT_EQ(_border, read.arg7());
+
+ EXPECT_TRUE(read.has_data());
+ EXPECT_EQ(read.ReferencedImage, read.data_type());
+ EXPECT_EQ(GL_RGBA, read.pixel_format());
+ EXPECT_EQ(GL_UNSIGNED_BYTE, read.pixel_type());
+ uint32_t dataLen = 0;
+ unsigned char * const data = tls.dbg->Decompress(read.data().data(),
+ read.data().length(), &dataLen);
+ ASSERT_EQ(sizeof(_pixels), dataLen);
+ for (unsigned i = 0; i < sizeof(_pixels) / sizeof(*_pixels); i++)
+ EXPECT_EQ(_pixels[i], ((const int *)data)[i]) << "xor with 0 ref is identity";
+ free(data);
+
+ Read(read);
+ EXPECT_EQ(read.glCopyTexImage2D, read.function());
+ EXPECT_EQ(read.AfterCall, read.type());
+
+ CheckNoAvailable();
+}
diff --git a/services/java/com/android/server/pm/Installer.java b/services/java/com/android/server/pm/Installer.java
index da3ebaf..d10aa97 100644
--- a/services/java/com/android/server/pm/Installer.java
+++ b/services/java/com/android/server/pm/Installer.java
@@ -225,10 +225,12 @@ class Installer {
return execute(builder.toString());
}
- public int remove(String name) {
+ public int remove(String name, int userId) {
StringBuilder builder = new StringBuilder("remove");
builder.append(' ');
builder.append(name);
+ builder.append(' ');
+ builder.append(userId);
return execute(builder.toString());
}
@@ -248,10 +250,30 @@ class Installer {
return execute(builder.toString());
}
- public int clearUserData(String name) {
+ public int createUserData(String name, int uid, int userId) {
+ StringBuilder builder = new StringBuilder("mkuserdata");
+ builder.append(' ');
+ builder.append(name);
+ builder.append(' ');
+ builder.append(uid);
+ builder.append(' ');
+ builder.append(userId);
+ return execute(builder.toString());
+ }
+
+ public int removeUserDataDirs(int userId) {
+ StringBuilder builder = new StringBuilder("rmuser");
+ builder.append(' ');
+ builder.append(userId);
+ return execute(builder.toString());
+ }
+
+ public int clearUserData(String name, int userId) {
StringBuilder builder = new StringBuilder("rmuserdata");
builder.append(' ');
builder.append(name);
+ builder.append(' ');
+ builder.append(userId);
return execute(builder.toString());
}
diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java
index a9d49b4..6e1093f 100644
--- a/services/java/com/android/server/pm/PackageManagerService.java
+++ b/services/java/com/android/server/pm/PackageManagerService.java
@@ -65,6 +65,7 @@ import android.content.pm.ProviderInfo;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.content.pm.Signature;
+import android.content.pm.UserInfo;
import android.net.Uri;
import android.os.Binder;
import android.os.Build;
@@ -208,6 +209,9 @@ public class PackageManagerService extends IPackageManager.Stub {
// This is where all application persistent data goes.
final File mAppDataDir;
+ // This is where all application persistent data goes for secondary users.
+ final File mUserAppDataDir;
+
// This is the object monitoring the framework dir.
final FileObserver mFrameworkInstallObserver;
@@ -359,6 +363,8 @@ public class PackageManagerService extends IPackageManager.Stub {
// Delay time in millisecs
static final int BROADCAST_DELAY = 10 * 1000;
+ final UserManager mUserManager;
+
final private DefaultContainerConnection mDefContainerConn =
new DefaultContainerConnection();
class DefaultContainerConnection implements ServiceConnection {
@@ -797,8 +803,11 @@ public class PackageManagerService extends IPackageManager.Stub {
File dataDir = Environment.getDataDirectory();
mAppDataDir = new File(dataDir, "data");
+ mUserAppDataDir = new File(dataDir, "user");
mDrmAppPrivateInstallDir = new File(dataDir, "app-private");
+ mUserManager = new UserManager(mInstaller, mUserAppDataDir);
+
if (mInstaller == null) {
// Make sure these dirs exist, when we are running in
// the simulator.
@@ -806,6 +815,7 @@ public class PackageManagerService extends IPackageManager.Stub {
File miscDir = new File(dataDir, "misc");
miscDir.mkdirs();
mAppDataDir.mkdirs();
+ mUserAppDataDir.mkdirs();
mDrmAppPrivateInstallDir.mkdirs();
}
@@ -974,7 +984,8 @@ public class PackageManagerService extends IPackageManager.Stub {
+ " no longer exists; wiping its data";
reportSettingsProblem(Log.WARN, msg);
if (mInstaller != null) {
- mInstaller.remove(ps.name);
+ mInstaller.remove(ps.name, 0);
+ mUserManager.removePackageForAllUsers(ps.name);
}
}
}
@@ -1059,10 +1070,12 @@ public class PackageManagerService extends IPackageManager.Stub {
void cleanupInstallFailedPackage(PackageSetting ps) {
Slog.i(TAG, "Cleaning up incompletely installed app: " + ps.name);
if (mInstaller != null) {
- int retCode = mInstaller.remove(ps.name);
+ int retCode = mInstaller.remove(ps.name, 0);
if (retCode < 0) {
Slog.w(TAG, "Couldn't remove app data directory for package: "
+ ps.name + ", retcode=" + retCode);
+ } else {
+ mUserManager.removePackageForAllUsers(ps.name);
}
} else {
//for emulator
@@ -1510,7 +1523,8 @@ public class PackageManagerService extends IPackageManager.Stub {
ps.pkg.applicationInfo.flags = ps.pkgFlags;
ps.pkg.applicationInfo.publicSourceDir = ps.resourcePathString;
ps.pkg.applicationInfo.sourceDir = ps.codePathString;
- ps.pkg.applicationInfo.dataDir = getDataPathForPackage(ps.pkg).getPath();
+ ps.pkg.applicationInfo.dataDir =
+ getDataPathForPackage(ps.pkg.packageName, 0).getPath();
ps.pkg.applicationInfo.nativeLibraryDir = ps.nativeLibraryPathString;
ps.pkg.mSetEnabled = ps.enabled;
ps.pkg.mSetStopped = ps.stopped;
@@ -2836,11 +2850,15 @@ public class PackageManagerService extends IPackageManager.Stub {
return true;
}
- private File getDataPathForPackage(PackageParser.Package pkg) {
- final File dataPath = new File(mAppDataDir, pkg.packageName);
- return dataPath;
+ File getDataPathForUser(int userId) {
+ return new File(mUserAppDataDir.getAbsolutePath() + File.separator + userId);
}
-
+
+ private File getDataPathForPackage(String packageName, int userId) {
+ return new File(mUserAppDataDir.getAbsolutePath() + File.separator
+ + userId + File.separator + packageName);
+ }
+
private PackageParser.Package scanPackageLI(PackageParser.Package pkg,
int parseFlags, int scanMode, long currentTime) {
File scanFile = new File(pkg.mScanPath);
@@ -3162,7 +3180,7 @@ public class PackageManagerService extends IPackageManager.Stub {
pkg.applicationInfo.dataDir = dataPath.getPath();
} else {
// This is a normal package, need to make its data directory.
- dataPath = getDataPathForPackage(pkg);
+ dataPath = getDataPathForPackage(pkg.packageName, 0);
boolean uidError = false;
@@ -3178,8 +3196,11 @@ public class PackageManagerService extends IPackageManager.Stub {
// If this is a system app, we can at least delete its
// current data so the application will still work.
if (mInstaller != null) {
- int ret = mInstaller.remove(pkgName);
+ int ret = mInstaller.remove(pkgName, 0);
if (ret >= 0) {
+ // TODO: Kill the processes first
+ // Remove the data directories for all users
+ mUserManager.removePackageForAllUsers(pkgName);
// Old data gone!
String msg = "System package " + pkg.packageName
+ " has changed from uid: "
@@ -3199,6 +3220,9 @@ public class PackageManagerService extends IPackageManager.Stub {
mLastScanError = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
return null;
}
+ // Create data directories for all users
+ mUserManager.installPackageForAllUsers(pkgName,
+ pkg.applicationInfo.uid);
}
}
if (!recovered) {
@@ -3235,11 +3259,13 @@ public class PackageManagerService extends IPackageManager.Stub {
if (mInstaller != null) {
int ret = mInstaller.install(pkgName, pkg.applicationInfo.uid,
pkg.applicationInfo.uid);
- if(ret < 0) {
+ if (ret < 0) {
// Error from installer
mLastScanError = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
return null;
}
+ // Create data directories for all users
+ mUserManager.installPackageForAllUsers(pkgName, pkg.applicationInfo.uid);
} else {
dataPath.mkdirs();
if (dataPath.exists()) {
@@ -5703,7 +5729,7 @@ public class PackageManagerService extends IPackageManager.Stub {
// Remember this for later, in case we need to rollback this install
String pkgName = pkg.packageName;
- boolean dataDirExists = getDataPathForPackage(pkg).exists();
+ boolean dataDirExists = getDataPathForPackage(pkg.packageName, 0).exists();
res.name = pkgName;
synchronized(mPackages) {
if (mSettings.mRenamedPackages.containsKey(pkgName)) {
@@ -6390,11 +6416,14 @@ public class PackageManagerService extends IPackageManager.Stub {
}
if ((flags&PackageManager.DONT_DELETE_DATA) == 0) {
if (mInstaller != null) {
- int retCode = mInstaller.remove(packageName);
+ int retCode = mInstaller.remove(packageName, 0);
if (retCode < 0) {
Slog.w(TAG, "Couldn't remove app data or cache directory for package: "
+ packageName + ", retcode=" + retCode);
// we don't consider this to be a failure of the core package deletion
+ } else {
+ // TODO: Kill the processes first
+ mUserManager.removePackageForAllUsers(packageName);
}
} else {
// for simulator
@@ -6654,7 +6683,7 @@ public class PackageManagerService extends IPackageManager.Stub {
}
}
if (mInstaller != null) {
- int retCode = mInstaller.clearUserData(packageName);
+ int retCode = mInstaller.clearUserData(packageName, 0); // TODO - correct userId
if (retCode < 0) {
Slog.w(TAG, "Couldn't remove cache files for package: "
+ packageName);
@@ -8015,4 +8044,17 @@ public class PackageManagerService extends IPackageManager.Stub {
android.provider.Settings.Secure.DEFAULT_INSTALL_LOCATION,
PackageHelper.APP_INSTALL_AUTO);
}
+
+ public UserInfo createUser(String name, int flags) {
+ UserInfo userInfo = mUserManager.createUser(name, flags, getInstalledApplications(0));
+ return userInfo;
+ }
+
+ public boolean removeUser(int userId) {
+ if (userId == 0) {
+ return false;
+ }
+ mUserManager.removeUser(userId);
+ return true;
+ }
}
diff --git a/services/java/com/android/server/pm/UserDetails.java b/services/java/com/android/server/pm/UserManager.java
index 2aeed7c..76fa5ab 100644
--- a/services/java/com/android/server/pm/UserDetails.java
+++ b/services/java/com/android/server/pm/UserManager.java
@@ -18,9 +18,13 @@ package com.android.server.pm;
import com.android.internal.util.FastXmlSerializer;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
import android.os.Environment;
import android.os.FileUtils;
+import android.os.SystemClock;
+import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
import android.util.Xml;
@@ -37,7 +41,7 @@ import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
-public class UserDetails {
+public class UserManager {
private static final String TAG_NAME = "name";
private static final String ATTR_FLAGS = "flags";
@@ -48,22 +52,27 @@ public class UserDetails {
private static final String TAG_USER = "user";
- private static final String TAG = "UserDetails";
+ private static final String LOG_TAG = "UserManager";
- private static final String USER_INFO_DIR = "system/users";
+ private static final String USER_INFO_DIR = "system" + File.separator + "users";
private static final String USER_LIST_FILENAME = "userlist.xml";
private SparseArray<UserInfo> mUsers;
private final File mUsersDir;
private final File mUserListFile;
+ private int[] mUserIds;
+
+ private Installer mInstaller;
+ private File mBaseUserPath;
/**
* Available for testing purposes.
*/
- UserDetails(File dataDir) {
+ UserManager(File dataDir, File baseUserPath) {
mUsersDir = new File(dataDir, USER_INFO_DIR);
mUsersDir.mkdirs();
+ mBaseUserPath = baseUserPath;
FileUtils.setPermissions(mUsersDir.toString(),
FileUtils.S_IRWXU|FileUtils.S_IRWXG
|FileUtils.S_IROTH|FileUtils.S_IXOTH,
@@ -72,8 +81,9 @@ public class UserDetails {
readUserList();
}
- public UserDetails() {
- this(Environment.getDataDirectory());
+ public UserManager(Installer installer, File baseUserPath) {
+ this(Environment.getDataDirectory(), baseUserPath);
+ mInstaller = installer;
}
public List<UserInfo> getUsers() {
@@ -84,6 +94,15 @@ public class UserDetails {
return users;
}
+ /**
+ * Returns an array of user ids. This array is cached here for quick access, so do not modify or
+ * cache it elsewhere.
+ * @return the array of user ids.
+ */
+ int[] getUserIds() {
+ return mUserIds;
+ }
+
private void readUserList() {
mUsers = new SparseArray<UserInfo>();
if (!mUserListFile.exists()) {
@@ -102,7 +121,7 @@ public class UserDetails {
}
if (type != XmlPullParser.START_TAG) {
- Slog.e(TAG, "Unable to read user list");
+ Slog.e(LOG_TAG, "Unable to read user list");
fallbackToSingleUser();
return;
}
@@ -116,6 +135,7 @@ public class UserDetails {
}
}
}
+ updateUserIds();
} catch (IOException ioe) {
fallbackToSingleUser();
} catch (XmlPullParserException pe) {
@@ -128,6 +148,7 @@ public class UserDetails {
UserInfo primary = new UserInfo(0, "Primary",
UserInfo.FLAG_ADMIN | UserInfo.FLAG_PRIMARY);
mUsers.put(0, primary);
+ updateUserIds();
writeUserList();
writeUser(primary);
@@ -164,7 +185,7 @@ public class UserDetails {
serializer.endDocument();
} catch (IOException ioe) {
- Slog.e(TAG, "Error writing user info " + userInfo.id + "\n" + ioe);
+ Slog.e(LOG_TAG, "Error writing user info " + userInfo.id + "\n" + ioe);
}
}
@@ -194,14 +215,13 @@ public class UserDetails {
serializer.startTag(null, TAG_USER);
serializer.attribute(null, ATTR_ID, Integer.toString(user.id));
serializer.endTag(null, TAG_USER);
- Slog.e(TAG, "Wrote user " + user.id + " to userlist.xml");
}
serializer.endTag(null, TAG_USERS);
serializer.endDocument();
} catch (IOException ioe) {
- Slog.e(TAG, "Error writing user list");
+ Slog.e(LOG_TAG, "Error writing user list");
}
}
@@ -222,14 +242,14 @@ public class UserDetails {
}
if (type != XmlPullParser.START_TAG) {
- Slog.e(TAG, "Unable to read user " + id);
+ Slog.e(LOG_TAG, "Unable to read user " + id);
return null;
}
if (type == XmlPullParser.START_TAG && parser.getName().equals(TAG_USER)) {
String storedId = parser.getAttributeValue(null, ATTR_ID);
if (Integer.parseInt(storedId) != id) {
- Slog.e(TAG, "User id does not match the file name");
+ Slog.e(LOG_TAG, "User id does not match the file name");
return null;
}
String flagString = parser.getAttributeValue(null, ATTR_FLAGS);
@@ -256,18 +276,25 @@ public class UserDetails {
return null;
}
- public UserInfo createUser(String name, int flags) {
- int id = getNextAvailableId();
- UserInfo userInfo = new UserInfo(id, name, flags);
- if (!createPackageFolders(id)) {
+ public UserInfo createUser(String name, int flags, List<ApplicationInfo> apps) {
+ int userId = getNextAvailableId();
+ UserInfo userInfo = new UserInfo(userId, name, flags);
+ File userPath = new File(mBaseUserPath, Integer.toString(userId));
+ if (!createPackageFolders(userId, userPath, apps)) {
return null;
}
- mUsers.put(id, userInfo);
+ mUsers.put(userId, userInfo);
writeUserList();
writeUser(userInfo);
+ updateUserIds();
return userInfo;
}
+ /**
+ * Removes a user and all data directories created for that user. This method should be called
+ * after the user's processes have been terminated.
+ * @param id the user's id
+ */
public void removeUser(int id) {
// Remove from the list
UserInfo userInfo = mUsers.get(id);
@@ -277,11 +304,58 @@ public class UserDetails {
// Remove user file
File userFile = new File(mUsersDir, id + ".xml");
userFile.delete();
+ // Update the user list
writeUserList();
+ // Remove the data directories for all packages for this user
removePackageFolders(id);
+ updateUserIds();
+ }
+ }
+
+ public void installPackageForAllUsers(String packageName, int uid) {
+ for (int userId : mUserIds) {
+ // Don't do it for the primary user, it will become recursive.
+ if (userId == 0)
+ continue;
+ mInstaller.createUserData(packageName, PackageManager.getUid(userId, uid),
+ userId);
+ }
+ }
+
+ public void clearUserDataForAllUsers(String packageName) {
+ for (int userId : mUserIds) {
+ // Don't do it for the primary user, it will become recursive.
+ if (userId == 0)
+ continue;
+ mInstaller.clearUserData(packageName, userId);
+ }
+ }
+
+ public void removePackageForAllUsers(String packageName) {
+ for (int userId : mUserIds) {
+ // Don't do it for the primary user, it will become recursive.
+ if (userId == 0)
+ continue;
+ mInstaller.remove(packageName, userId);
+ }
+ }
+
+ /**
+ * Caches the list of user ids in an array, adjusting the array size when necessary.
+ */
+ private void updateUserIds() {
+ if (mUserIds == null || mUserIds.length != mUsers.size()) {
+ mUserIds = new int[mUsers.size()];
+ }
+ for (int i = 0; i < mUsers.size(); i++) {
+ mUserIds[i] = mUsers.keyAt(i);
}
}
+ /**
+ * Returns the next available user id, filling in any holes in the ids.
+ * @return
+ */
private int getNextAvailableId() {
int i = 0;
while (i < Integer.MAX_VALUE) {
@@ -293,13 +367,35 @@ public class UserDetails {
return i;
}
- private boolean createPackageFolders(int id) {
- // TODO: Create data directories for all the packages for a new user, w/ specified user id.
+ private boolean createPackageFolders(int id, File userPath, final List<ApplicationInfo> apps) {
+ // mInstaller may not be available for unit-tests.
+ if (mInstaller == null || apps == null) return true;
+
+ final long startTime = SystemClock.elapsedRealtime();
+ // Create the user path
+ userPath.mkdir();
+ FileUtils.setPermissions(userPath.toString(), FileUtils.S_IRWXU | FileUtils.S_IRWXG
+ | FileUtils.S_IXOTH, -1, -1);
+
+ // Create the individual data directories
+ for (ApplicationInfo app : apps) {
+ if (app.uid > android.os.Process.FIRST_APPLICATION_UID
+ && app.uid < PackageManager.PER_USER_RANGE) {
+ mInstaller.createUserData(app.packageName,
+ PackageManager.getUid(id, app.uid), id);
+ }
+ }
+ final long stopTime = SystemClock.elapsedRealtime();
+ Log.i(LOG_TAG,
+ "Time to create " + apps.size() + " packages = " + (stopTime - startTime) + "ms");
return true;
}
private boolean removePackageFolders(int id) {
- // TODO: Remove all the data directories for the specified user.
+ // mInstaller may not be available for unit-tests.
+ if (mInstaller == null) return true;
+
+ mInstaller.removeUserDataDirs(id);
return true;
}
}
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserDetailsTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
index 7b77aac..e8188e7 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserDetailsTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerTest.java
@@ -16,7 +16,7 @@
package com.android.server.pm;
-import com.android.server.pm.UserDetails;
+import com.android.server.pm.UserManager;
import android.content.pm.UserInfo;
import android.os.Debug;
@@ -25,23 +25,24 @@ import android.test.AndroidTestCase;
import java.util.List;
-/** Test {@link UserDetails} functionality. */
-public class UserDetailsTest extends AndroidTestCase {
+/** Test {@link UserManager} functionality. */
+public class UserManagerTest extends AndroidTestCase {
- UserDetails mDetails = null;
+ UserManager mUserManager = null;
@Override
public void setUp() throws Exception {
- mDetails = new UserDetails(Environment.getExternalStorageDirectory());
+ mUserManager = new UserManager(Environment.getExternalStorageDirectory(),
+ Environment.getExternalStorageDirectory());
}
@Override
public void tearDown() throws Exception {
- List<UserInfo> users = mDetails.getUsers();
+ List<UserInfo> users = mUserManager.getUsers();
// Remove all except the primary user
for (UserInfo user : users) {
if (!user.isPrimary()) {
- mDetails.removeUser(user.id);
+ mUserManager.removeUser(user.id);
}
}
}
@@ -51,9 +52,9 @@ public class UserDetailsTest extends AndroidTestCase {
}
public void testAddUser() throws Exception {
- final UserDetails details = mDetails;
+ final UserManager details = mUserManager;
- UserInfo userInfo = details.createUser("Guest 1", UserInfo.FLAG_GUEST);
+ UserInfo userInfo = details.createUser("Guest 1", UserInfo.FLAG_GUEST, null);
assertTrue(userInfo != null);
List<UserInfo> list = details.getUsers();
@@ -70,10 +71,10 @@ public class UserDetailsTest extends AndroidTestCase {
}
public void testAdd2Users() throws Exception {
- final UserDetails details = mDetails;
+ final UserManager details = mUserManager;
- UserInfo user1 = details.createUser("Guest 1", UserInfo.FLAG_GUEST);
- UserInfo user2 = details.createUser("User 2", UserInfo.FLAG_ADMIN);
+ UserInfo user1 = details.createUser("Guest 1", UserInfo.FLAG_GUEST, null);
+ UserInfo user2 = details.createUser("User 2", UserInfo.FLAG_ADMIN, null);
assertTrue(user1 != null);
assertTrue(user2 != null);
@@ -84,9 +85,9 @@ public class UserDetailsTest extends AndroidTestCase {
}
public void testRemoveUser() throws Exception {
- final UserDetails details = mDetails;
+ final UserManager details = mUserManager;
- UserInfo userInfo = details.createUser("Guest 1", UserInfo.FLAG_GUEST);
+ UserInfo userInfo = details.createUser("Guest 1", UserInfo.FLAG_GUEST, null);
details.removeUser(userInfo.id);
@@ -94,7 +95,7 @@ public class UserDetailsTest extends AndroidTestCase {
}
private boolean findUser(int id) {
- List<UserInfo> list = mDetails.getUsers();
+ List<UserInfo> list = mUserManager.getUsers();
for (UserInfo user : list) {
if (user.id == id) {
diff --git a/wifi/java/android/net/wifi/WifiStateMachine.java b/wifi/java/android/net/wifi/WifiStateMachine.java
index f7157d4..16611d8 100644
--- a/wifi/java/android/net/wifi/WifiStateMachine.java
+++ b/wifi/java/android/net/wifi/WifiStateMachine.java
@@ -1978,17 +1978,6 @@ public class WifiStateMachine extends StateMachine {
boolean eventLoggingEnabled = true;
switch(message.what) {
case CMD_STOP_SUPPLICANT: /* Supplicant stopped by user */
- Log.d(TAG, "stopping supplicant");
- if (!WifiNative.stopSupplicant()) {
- Log.e(TAG, "Failed to stop supplicant, issue kill");
- WifiNative.killSupplicant();
- }
- mNetworkInfo.setIsAvailable(false);
- handleNetworkDisconnect();
- setWifiState(WIFI_STATE_DISABLING);
- sendSupplicantConnectionChangedBroadcast(false);
- mSupplicantStateTracker.sendMessage(CMD_RESET_SUPPLICANT_STATE);
- mWpsStateMachine.sendMessage(CMD_RESET_WPS_STATE);
transitionTo(mSupplicantStoppingState);
break;
case SUP_DISCONNECTION_EVENT: /* Supplicant connection lost */
@@ -2089,6 +2078,17 @@ public class WifiStateMachine extends StateMachine {
public void enter() {
if (DBG) Log.d(TAG, getName() + "\n");
EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName());
+ Log.d(TAG, "stopping supplicant");
+ if (!WifiNative.stopSupplicant()) {
+ Log.e(TAG, "Failed to stop supplicant, issue kill");
+ WifiNative.killSupplicant();
+ }
+ mNetworkInfo.setIsAvailable(false);
+ handleNetworkDisconnect();
+ setWifiState(WIFI_STATE_DISABLING);
+ sendSupplicantConnectionChangedBroadcast(false);
+ mSupplicantStateTracker.sendMessage(CMD_RESET_SUPPLICANT_STATE);
+ mWpsStateMachine.sendMessage(CMD_RESET_WPS_STATE);
}
@Override
public boolean processMessage(Message message) {