“Static storage paths aren’t available from AID_SYSTEM” 错误是 Android 4.2.2 系统中通过声明 android:sharedUserId=”android.uid.system”,并且通过系统对应的 platform.x509.pem 和 platform.pk8 文件签名的应用读写 Micro SD 卡时发生的。
Runtime Environment
OS: Windows 8.1
IDE: ADT Bundle v22.6.2
Device: Android Official Simulator
说明:为了方便读者读到我这篇文章的时候,能够对文章内容进行验证,我将测试环境换成了 Android 官方的模拟器,因为本应用采用了系统对应的 platform.x509.pem 和 platform.pk8 文件签名,而模拟器的对应的这两个文件,读者是方便拿到的,而我自己开发应用是运行在定制的 Android Pad 上面,即使我公开了 platform.x509.pem 和 platform.pk8 这两个文件,读者也是无法测试的,因为这两个文件根据 ROM 的不同而不同的。
适用于 Android 官方模拟器的 platform.x509.pem 和 platform.pk8 这两个文件可以在GitHub上下载:https://github.com/android/platform_build/tree/master/target/product/security
签名工具 signapk.jar 下载:https://github.com/ifeegoo/signapk.jar 用到的命令如下;
// 给未签名的apk文件用系统对应的 platform.x509.pem 和 platform.pk8 两个文件签名 java -jar signapk.jar platform.x509.pem platform.pk8 debug.apk debug_signed.apk // ADB 安装和卸载的命令 adb install debug_signed.apk adb uninstall com.ifeegoo.debug
备注:用系统的 platform.x509.pem, platform.pk8 文件对应用进行签名方式如下: 1. 用 eclipse 导出未签名的应用 debug.apk 。 2. 用签名工具 signapk.jar 和系统对应的 platform.x509.pem, platform.pk8 文件通过以下命令得到具备系统签名的debug_signed.apk 。 java -jar signapk.jar platform.x509.pem platform.pk8 debug.apk debug_signed.apk
运行程序的时候,发现 “Static storage paths aren’t available from AID_SYSTEM” 错误,并且发现写入 Micro SD 卡 Permission denied , 错误日志如下:
A/Environment(1261): Static storage paths aren't available from AID_SYSTEM A/Environment(1261): java.lang.Throwable A/Environment(1261): at android.os.Environment.throwIfSystem(Environment.java:637) A/Environment(1261): at android.os.Environment.getExternalStorageDirectory(Environment.java:316) A/Environment(1261): at com.ifeegoo.debug.utils.FileManager.saveToExternalStorageDirectory(FileManager.java:18) A/Environment(1261): at com.ifeegoo.debug.activities.MainActivity$1.run(MainActivity.java:23) A/Environment(1261): at java.lang.Thread.run(Thread.java:856) W/System.err(1261): java.io.FileNotFoundException: /mnt/sdcard/ifeegoo.txt: open failed: EACCES (Permission denied) W/System.err(1261): at libcore.io.IoBridge.open(IoBridge.java:416) W/System.err(1261): at java.io.FileOutputStream.<init>(FileOutputStream.java:88) W/System.err(1261): at java.io.FileOutputStream.<init>(FileOutputStream.java:73) W/System.err(1261): at com.ifeegoo.debug.utils.FileManager.saveToExternalStorageDirectory(FileManager.java:17) W/System.err(1261): at com.ifeegoo.debug.activities.MainActivity$1.run(MainActivity.java:23) W/System.err(1261): at java.lang.Thread.run(Thread.java:856) W/System.err(1261): Caused by: libcore.io.ErrnoException: open failed: EACCES (Permission denied) W/System.err(1261): at libcore.io.Posix.open(Native Method) I/Choreographer(1261): Skipped 63 frames! The application may be doing too much work on its main thread. W/System.err(1261): at libcore.io.BlockGuardOs.open(BlockGuardOs.java:110) W/System.err(1261): at libcore.io.IoBridge.open(IoBridge.java:400) W/System.err(1261): ... 5 more
从 AID_SYSTEM,throwIfSystem,SYSTEM_UID 等字眼中可以看出当前应用和系统有关。同时从 /mnt/sdcard,Permission denied 等字眼中可以看出当前应用被禁止Micro SD 卡写入。分析: 1. 由于当前应用涉及到一些和系统权限相关的功能,在配置文件中有 android:sharedUserId=”android.uid.system” 的声明,并且通过系统对应的 platform.x509.pem, platform.pk8 文件来对应用签名。 2. 应用会将一些数据保存在 Micro SD 卡上。
应用通过系统对应的 platform.x509.pem 和 platform.pk8 文件签名,AndroidManifest.xml 中有声明 android:sharedUserId=”android.uid.system” 和写入 Micro SD卡权限:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.ifeegoo.debug"
android:sharedUserId="android.uid.system"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="19" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<application
android:allowBackup="true"
android:icon="@drawable/icon_launcher" >
<activity
android:name="com.ifeegoo.debug.activities.MainActivity" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
通过错误日志第3行的提示,查找源代码,发现在 android.os.Environment 这个类中,有以下方法产生以上错误:
private static void throwIfSystem()
{
if (Process.myUid() == Process.SYSTEM_UID)
{
Log.wtf(TAG, "Static storage paths aren't available from AID_SYSTEM", new Throwable());
}
}
当 Process.myUid() 的值与 Process.SYSTEM_UID 的值相等时,会抛出异常!查看 android.os.Process 这个类,有如下说明,由于个人英语水平和Linux知识有限,不能理解。但是可以通过另外的方法来验证以上条件是否成立。
/**
* Defines the UID/GID under which system code runs.
*/
public static final int SYSTEM_UID = 1000;
/**
* Returns the identifier of this process's uid. This is the kernel uid
* that the process is running under, which is the identity of its
* app-specific sandbox. It is different from {@link #myUserHandle} in that
* a uid identifies a specific app sandbox in a specific user.
*/
public static final native int myUid();
通过应用内打印日志的方式,发现如下结果:
D/Process(1806): Process.myUid() = 1000
凭借着自己的对进程ID的一点认识,觉得这个值会不会变呢?会不会这次恰巧是1000呢,通过5次的验证,发现如下结果: 备注:每一次都是卸载重新安装重启之后打开应用测试的结果!
08-28 09:31:26.149: D/Process(995): Process.myUid() = 1000 08-28 09:35:18.577: D/Process(718): Process.myUid() = 1000 08-28 09:38:43.600: D/Process(722): Process.myUid() = 1000 08-28 09:41:40.382: D/Process(730): Process.myUid() = 1000 08-28 09:44:17.761: D/Process(724): Process.myUid() = 1000
通过 adb shell 的 ps 命令,查看到本应用 com.ifeegoo.debug 的 USER 标识为 system :
C:\>adb shell root@android:/ # ps ps USER PID PPID VSIZE RSS WCHAN PC NAME root 1 0 296 208 c0098770 0000e840 S /init root 2 0 0 0 c005048c 00000000 S kthreadd root 3 2 0 0 c0042268 00000000 S ksoftirqd/0 root 4 2 0 0 c004ce30 00000000 S events/0 root 5 2 0 0 c004ce30 00000000 S khelper root 6 2 0 0 c004ce30 00000000 S suspend root 7 2 0 0 c004ce30 00000000 S kblockd/0 root 8 2 0 0 c004ce30 00000000 S cqueue root 9 2 0 0 c016f7c4 00000000 S kseriod root 10 2 0 0 c004ce30 00000000 S kmmcd root 11 2 0 0 c006f36c 00000000 S pdflush root 12 2 0 0 c006f36c 00000000 S pdflush root 13 2 0 0 c007340c 00000000 S kswapd0 root 14 2 0 0 c004ce30 00000000 S aio/0 root 25 2 0 0 c016d0f8 00000000 S mtdblockd root 26 2 0 0 c004ce30 00000000 S kstriped root 27 2 0 0 c004ce30 00000000 S hid_compat root 28 2 0 0 c004ce30 00000000 S rpciod/0 root 29 2 0 0 c0189ddc 00000000 S mmcqd root 30 1 292 172 c0098770 0000e840 S /sbin/ueventd system 31 1 836 348 c0195c08 40036fc0 S /system/bin/servicemanager root 32 1 4008 860 ffffffff 4003e76c S /system/bin/vold root 34 1 8632 1252 ffffffff 4006a76c S /system/bin/netd root 35 1 880 388 c01a10a0 40037a70 S /system/bin/debuggerd radio 36 1 5468 836 ffffffff 4003776c S /system/bin/rild system 37 1 13388 3196 ffffffff 4006bfc0 S /system/bin/surfaceflinger root 38 1 165936 33672 ffffffff 400370e4 S zygote drm 39 1 6564 2320 ffffffff 400befc0 S /system/bin/drmserver media 40 1 23040 6404 ffffffff 4008cfc0 S /system/bin/mediaserver install 41 1 848 472 c021db90 40036d50 S /system/bin/installd keystore 42 1 1796 892 c01a10a0 40037a70 S /system/bin/keystore root 43 1 828 372 c00b4eb0 40037ebc S /system/bin/qemud shell 46 1 764 460 c0148178 40031d50 S /system/bin/sh root 47 1 5524 300 ffffffff 00015ef0 S /sbin/adbd system 278 38 248768 43040 ffffffff 40036fc0 S system_server u0_a23 399 38 182520 33800 ffffffff 40037ebc S com.android.systemui u0_a24 426 38 177720 20876 ffffffff 40037ebc S com.android.inputmethod.latin radio 444 38 197980 26020 ffffffff 40037ebc S com.android.phone system 458 38 183816 19280 ffffffff 40037ebc S com.android.settings u0_a4 503 38 201724 33892 ffffffff 40037ebc S android.process.acore u0_a5 519 38 194748 35200 ffffffff 40037ebc S com.android.launcher u0_a32 528 38 176204 17928 ffffffff 40037ebc S com.android.music u0_a10 558 38 180700 21524 ffffffff 40037ebc S android.process.media u0_a1 573 38 177700 17544 ffffffff 40037ebc S com.android.quicksearchbox u0_a4 620 38 183928 21000 ffffffff 40037ebc S com.android.contacts u0_a16 642 38 174268 16404 ffffffff 40037ebc S com.android.location.fused u0_a3 657 38 180948 20592 ffffffff 40037ebc S com.android.mms u0_a6 681 38 178692 19916 ffffffff 40037ebc S com.android.deskclock u0_a28 703 38 183548 17936 ffffffff 40037ebc S com.android.exchange u0_a33 725 38 180920 19316 ffffffff 40037ebc S com.android.providers.calendar u0_a26 741 38 186104 20064 ffffffff 40037ebc S com.android.calendar u0_a9 933 38 176340 16664 ffffffff 40037ebc S com.android.defcontainer u0_a14 951 38 174256 16012 ffffffff 40037ebc S com.svox.pico root 984 47 752 432 c002a7a0 4003294c S /system/bin/sh root 986 984 720 412 c0098770 400370e4 S logcat system 995 38 175676 20056 ffffffff 40037ebc S com.ifeegoo.debug root 1014 47 764 480 c002a7a0 4003294c S /system/bin/sh root 1019 1014 1092 436 00000000 40036d50 R ps
其它4次的结果:
C:\>adb shell root@android:/ # ps ps USER PID PPID VSIZE RSS WCHAN PC NAME *** system 718 38 175676 20060 ffffffff 40037ebc S com.ifeegoo.debug system 722 38 175676 20060 ffffffff 40037ebc S com.ifeegoo.debug system 730 38 175676 20060 ffffffff 40037ebc S com.ifeegoo.debug system 724 38 175676 20068 ffffffff 40037ebc S com.ifeegoo.debug
去掉应用 AndroidManifest.xml 文件中的 android:sharedUserId=”android.uid.system” 的声明,还是用系统对应的 platform.x509.pem, platform.pk8 文件对应用进行签名,得到的测试结果:
08-28 10:19:01.193: D/Process(1448): Process.myUid() = 10046 08-28 10:22:45.724: D/Process(705): Process.myUid() = 10046 08-28 10:24:54.461: D/Process(697): Process.myUid() = 10046 08-28 10:27:49.170: D/Process(699): Process.myUid() = 10046 08-28 10:29:59.600: D/Process(708): Process.myUid() = 10046
C:\>adb shell root@android:/ # ps ps USER PID PPID VSIZE RSS WCHAN PC NAME *** u0_a46 1448 38 175676 19956 ffffffff 40037ebc S com.ifeegoo.debug u0_a46 705 38 175676 19968 ffffffff 40037ebc S com.ifeegoo.debug u0_a46 697 38 175676 19968 ffffffff 40037ebc S com.ifeegoo.debug u0_a46 699 38 175676 19960 ffffffff 40037ebc S com.ifeegoo.debug u0_a46 773 38 175676 19968 ffffffff 40037ebc S com.ifeegoo.debug
从以上结果可以看到,去掉应用 AndroidManifest.xml 文件中的 android:sharedUserId=”android.uid.system” 的声明,然后 Process.myUid() 的值也不等于 1000,同时 USER 标识也变成了 u0_a46,再者,android.os.Environment 这个类中的 throwIfSystem() 方法是在以下方法中调用:
public static File getExternalStorageDirectory()
{
throwIfSystem();
return sCurrentUser.getExternalStorageDirectory();
}
同时,通过查看源代码和逐一测试得到以下结果: —Android 4.2.2 以下版本是不会报 “Static storage paths aren’t available from AID_SYSTEM” 错误,但是会报文件写入 Permission denied; —Android 4.3 不会报 “Static storage paths aren’t available from AID_SYSTEM” 错误,但是会报文件写入 Permission denied; —Android 4.4.2 不会报 “Static storage paths aren’t available from AID_SYSTEM” 错误,并且能够写入成功!!!!!!
/**
* Android 4.2.2 以下
*
*/
public class Environment
{
public static File getExternalStorageDirectory()
{
return EXTERNAL_STORAGE_DIRECTORY;
}
}
/**
* Android 4.2.2
*
*/
public class Environment
{
public static File getExternalStorageDirectory()
{
throwIfSystem();
return sCurrentUser.getExternalStorageDirectory();
}
private static void throwIfSystem()
{
if (Process.myUid() == Process.SYSTEM_UID)
{
Log.wtf(TAG,
"Static storage paths aren't available from AID_SYSTEM",
new Throwable());
}
}
}
/**
* Android 4.3 / Android 4.4.2
*/
public class Environment
{
private static boolean sUserRequired;
public static File getExternalStorageDirectory()
{
throwIfUserRequired();
return sCurrentUser.getExternalStorageDirectory();
}
/** {@hide} */
public static void setUserRequired(boolean userRequired)
{
sUserRequired = userRequired;
}
private static void throwIfUserRequired()
{
if (sUserRequired)
{
Log.wtf(TAG,
"Path requests must specify a user by using UserEnvironment",
new Throwable());
}
}
}
从以上 Android 4.3 / Android 4.4.2 代码可以看到,如果不调用隐藏方法 setUserRequired(boolean userRequired) 的话,就不会抛出异常。由此可以得到以下结论: “Static storage paths aren’t available from AID_SYSTEM” 错误是 Android 4.2.2 系统中通过声明 android:sharedUserId=”android.uid.system”,并且通过系统的 platform.x509.pem 和 platform.pk8 文件签名的应用读写Micro SD卡时发生的。
探讨解决方法: 由于此应用中的某些具有系统权限功能的原因,声明 android:sharedUserId=”android.uid.system”,并且通过系统的 platform.x509.pem 和 platform.pk8 文件签名的这两个前提是不会变的!
1. 由于涉及到向 Micro SD 卡写入数据,是调用 android.os.Environment 类中的 getExternalStorageDirectory() 方法得到外部存储路径的,我们可不可以采用 “hardcode” 方式直接写出已知外部存储路径(备注:此应用运行在定制的 Android Pad 上!),这样我们是不是就可以解决这个问题呢?
/**
* @author ifeegoo www.ifeegoo.com
*/
public static void saveToExternalStorageDirectory(String filename,
String content)
{
if ((filename == null) || (content == null))
{
return;
}
FileOutputStream fileOutputStream = null;
try
{
// 将 Environment.getExternalStorageDirectory().getAbsolutePath() 替换成 "mnt/sdcard"
fileOutputStream = new FileOutputStream(new File("mnt/sdcard",
filename));
fileOutputStream.write(content.getBytes());
} catch (FileNotFoundException e)
{
e.printStackTrace();
} catch (IOException e)
{
e.printStackTrace();
} finally
{
try
{
if (fileOutputStream != null)
{
fileOutputStream.close();
}
} catch (IOException e)
{
e.printStackTrace();
}
}
}
W/System.err(5089): java.io.FileNotFoundException: /mnt/sdcard/ifeegoo.txt: open failed: EACCES (Permission denied) W/System.err(5089): at libcore.io.IoBridge.open(IoBridge.java:416) W/System.err(5089): at java.io.FileOutputStream.<init>(FileOutputStream.java:88) W/System.err(5089): at java.io.FileOutputStream.<init>(FileOutputStream.java:73) W/System.err(5089): at com.ifeegoo.debug.utils.FileManager.saveToExternalStorageDirectory(FileManager.java:28) W/System.err(5089): at com.ifeegoo.debug.activities.MainActivity$1.run(MainActivity.java:23) W/System.err(5089): at java.lang.Thread.run(Thread.java:856) W/System.err(5089): Caused by: libcore.io.ErrnoException: open failed: EACCES (Permission denied) W/System.err(5089): at libcore.io.Posix.open(Native Method)
在Android 4.2.2 中测试,从以上日志可以看到,虽然没有报 “Static storage paths aren’t available from AID_SYSTEM” 错误,但是依然会有无法写入文件,Permission denied 。检查是否写入文件:
C:\>adb shell root@android:/ # cd /mnt/sdcard cd /mnt/sdcard root@android:/mnt/sdcard # ls ls Alarms Android DCIM Download LOST.DIR Movies Music Notifications Pictures Podcasts Ringtones www root@android:/mnt/sdcard #
依然无法写入到 Micro SD 卡上,至于在哪个地方控制了这个权限问题,暂时不明(个人猜测是类似于 Linux 权限控制问题)。所以在 Android 4.2.2 中,用 “hardcode” 方法将外部存储目录由 Environment.getExternalStorageDirectory().getAbsolutePath() 改成 “mnt/sdcard” ,是可以避免 “Static storage paths aren’t available from AID_SYSTEM” 错误,但是依然会出现 Micro SD 卡写入 Permission denied !
2. 由于涉及到向 Micro SD 卡写入数据,如果说应用的数据不必非要写入 Micro SD 卡的话,我们尝试写入到应用内部存储目录:
/**
* @author ifeegoo www.ifeegoo.com
*/
public static void saveToInternalFilesDirectory(Context context,
String filename, String content)
{
if ((context == null) || (filename == null) || (content == null))
{
return;
}
FileOutputStream fileOutputStream = null;
try
{
fileOutputStream = new FileOutputStream(new File(context
.getFilesDir().getAbsolutePath(), filename));
fileOutputStream.write(content.getBytes());
} catch (FileNotFoundException e)
{
e.printStackTrace();
} catch (IOException e)
{
e.printStackTrace();
} finally
{
try
{
if (fileOutputStream != null)
{
fileOutputStream.close();
}
} catch (IOException e)
{
e.printStackTrace();
}
}
}
检查是否写入到内部存储目录:
C:\>adb shell root@android:/ # cd /data/data/com.ifeegoo.debug/files cd /data/data/com.ifeegoo.debug/files root@android:/data/data/com.ifeegoo.debug/files # ls ls ifeegoo.txt root@android:/data/data/com.ifeegoo.debug/files # cat ifeegoo.txt cat ifeegoo.txt www.ifeegoo.com root@android:/data/data/com.ifeegoo.debug/files #
在Android 4.2.2 中,如果说应用的数据不必非要写入 Micro SD 卡的话,我们可以将数据写入到应用内部存储目录 /data/data/package name/files/ ,这样我们就可以解决报 “Static storage paths aren’t available from AID_SYSTEM” 错误,以及写入 Micro SD 卡时 Permission denied !
3. Android 4.2.2 API 中,从 android.os.Environment 这个类中,发现有一个隐藏的静态内部类,里面也有一个方法为 getExternalStorageDirectory() ,同时这个方法并没有调用 throwIfSystem() 方法,如果说能够用反射来调用这个里面的方法,是不是就可以解决这个问题呢?
public class Environment
{
/** {@hide} */
public static class UserEnvironment
{
public File getExternalStorageDirectory()
{
return mExternalStorage;
}
}
}
/**
* UserEnvironment 类的构造方法中需要传入 userId,不明白这个 userId,暂且传入一个int数值0
*
* @author ifeegoo www.ifeegoo.com
*
*/
public class ReflectManager
{
public static String getExternalStoragePath()
{
try
{
Class<?> klass = Class
.forName("android.os.Environment$UserEnvironment");
Method method = klass
.getDeclaredMethod("getExternalStorageDirectory");
Object object = method.invoke(klass.getConstructor(int.class)
.newInstance(new Object[] { 0 }));
if (object != null)
{
return object.toString();
}
} catch (ClassNotFoundException e)
{
e.printStackTrace();
} catch (NoSuchMethodException e)
{
e.printStackTrace();
} catch (IllegalAccessException e)
{
e.printStackTrace();
} catch (IllegalArgumentException e)
{
e.printStackTrace();
} catch (InvocationTargetException e)
{
e.printStackTrace();
} catch (InstantiationException e)
{
e.printStackTrace();
}
return null;
}
}
这样是可以避免抛出异常,但是以上方法其实和第一种方法一样,虽然避免了采用那种 “hardcode” 方式来获取外部存储路径,但是依然写入 Micro SD 卡,出现 Permission denied ,依然是权限问题!
Android 4.2.2 API 中,调用 android.os.Environment 类中的隐藏类 UserEnvironment 中的 getExternalStorageDirectory() 方法,虽然可以避免 “Static storage paths aren’t available from AID_SYSTEM” 错误,但是依然会出现 Micro SD 卡写入 Permission denied !
4. 从上面的研究看来,即使不报 “Static storage paths aren’t available from AID_SYSTEM” 错误,也会出现读写权限的问题,那可不可以自己在应用中,自己更改 Micro SD 卡的读写权限呢?
/**
*
* @author ifeegoo www.ifeegoo.com
*
*/
public class AdbShellManager
{
public static String chmod777(String path)
{
String[] args = { "chmod", "777", path, "\n" };
ProcessBuilder processBuilder = new ProcessBuilder(args);
String result = null;
Process process = null;
InputStream standardErrorInputStream = null;
InputStream standardOutInputStream = null;
try
{
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
int read = -1;
if (processBuilder != null)
{
process = processBuilder.start();
}
standardErrorInputStream = process.getErrorStream();
if (standardErrorInputStream != null)
{
while ((read = standardErrorInputStream.read()) != -1)
{
byteArrayOutputStream.write(read);
}
}
byteArrayOutputStream.write('\n');
standardOutInputStream = process.getInputStream();
if (standardOutInputStream != null)
{
while ((read = standardOutInputStream.read()) != -1)
{
byteArrayOutputStream.write(read);
}
}
result = new String(byteArrayOutputStream.toByteArray());
} catch (IOException e)
{
e.printStackTrace();
} catch (Exception e)
{
e.printStackTrace();
} finally
{
try
{
if (standardErrorInputStream != null)
{
standardErrorInputStream.close();
}
if (standardOutInputStream != null)
{
standardOutInputStream.close();
}
} catch (IOException e)
{
e.printStackTrace();
}
if (process != null)
{
process.destroy();
}
}
return result;
}
}
在以上 AdbShellManager 类中的 chmod777(String path) 方法传入 “mnt/sdcard” ,在 Android 4.2.2 系统中的测试结果如下:
08-31 23:04:25.404: D/chmod777(971): Unable to chmod 08-31 23:04:25.404: D/chmod777(971): : No such file or directory
以上结果提示 “Unable to chmod” , “No such file or directory”。我们查看目录是否存在:
C:\>adb shell root@android:/mnt/sdcard # cd /mnt/sdcard cd /mnt/sdcard root@android:/mnt/sdcard # ls ls Alarms Android DCIM Download LOST.DIR Movies Music Notifications Pictures Podcasts Ringtones www
可以看到 “mnt/sdcard” 目录是存在的,但是不知道为什么提示“无此文件或目录”,难道是因为更改权限没有成功或者访问权限的问题?尝试先请求 root 权限,将 AdbShellManager 类中的 chmod777(String path) 方法中的 String[] args = { “chmod”, “777”, path, “\n” }; 更改成 String[] args = { “su”, “\n”, “chmod”, “777”, path, “\n” }; 测试结果如下:
08-31 23:22:13.765: D/chmod777(1337): uid 1000 not allowed to su
以上结果提示拒绝获得 root 权限;对比模拟器和定制的 Android Pad 两个版本的 Android 4.2.2 中 Micro SD 卡写入权限:
// 在当前路径下用 ls -l 可以查看当前路径的读写权限 // Android Simulator d---rwxr-x system sdcard_rw 1970-01-01 00:00 sdcard // Android Pad drwxrwxr-x system sdcard_rw 2014-09-01 19:07 sdcard
5. 既然本应用是运行在定制的 Android Pad 上,通过源代码可以看到,只有 Android 4.2.2 才会出现报 “Static storage paths aren’t available from AID_SYSTEM” 错误,那么我们可以将当前运行在 Android Pad 上的 Android 4.2.2 系统换成 Android 4.4.2,这样是否就可以解决这个问题了呢?
C:\>adb shell root@android:/mnt/sdcard # cd /mnt/sdcard cd /mnt/sdcard root@generic:/mnt/sdcard # ls ls Alarms Android DCIM Download LOST.DIR Movies Music Notifications Pictures Podcasts Ringtones ifeegoo.txt www root@generic:/mnt/sdcard # cat ifeegoo.txt cat ifeegoo.txt www.ifeegoo.com root@generic:/mnt/sdcard #
再确认下应用的 USER 标识:
C:\>adb shell root@generic:/ # ps ps USER PID PPID VSIZE RSS WCHAN PC NAME *** system 1270 54 214952 20884 ffffffff b6efe5cc S com.ifeegoo.debug
根据测试结果,可以看出,在 Android 4.4.2 系统中,通过声明 android:sharedUserId=”android.uid.system”,并且通过系统的 platform.x509.pem 和 platform.pk8 文件签名的应用读写 Micro SD 卡是没有问题的,既然本应用是运行在定制的 Andorid Pad 上,因此我们可以考虑将 Android 4.2.2 系统更改成 Android 4.4.2 系统!
6. 既然本应用是运行在定制的 Android Pad 上,因为在 Android 4.2.2 系统中的 android.os.Environment 类中的 getExternalStorageDirectory() 方法调用了 throwIfSystem() 方法,导致了报 “Static storage paths aren’t available from AID_SYSTEM” 错误,同时由于系统限制了通过声明 android:sharedUserId=”android.uid.system”,并且通过系统的 platform.x509.pem 和 platform.pk8 文件签名的应用读写 Micro SD 卡权限,那么我们可否通过更改框架层的代码,并且更改应用对 Micro SD 卡读写权限来解决以上问题呢?
答案是肯定的!
由于应用运行在定制的 Android Pad 上,因此我们可以通过更改 Android 4.2.2 系统框架层的代码,并且更改应用对 Micro SD 卡读写权限来解决通过声明 android:sharedUserId=”android.uid.system”,并且通过系统的 platform.x509.pem 和 platform.pk8 文件签名的应用报 “Static storage paths aren’t available from AID_SYSTEM” 错误和无法写入Micro SD 卡的问题!最终这个问题也是通过这样的方式解决的!
备注:由于我的应用运行在定制的 Android Pad 上,并且这个 Android Pad 是提供解决方案的公司做 Framework 层的工程师来修改源代码的,我本人是做应用层的,对这个修改框架层的代码也不懂,但是在 CSDN 上发现一篇作者 ID 为 echojiangyq_fight 写的一篇文章,恰好是我这个问题,我将文章内容转载过来,以供参考(以下内容中没有看到作者提到是否处理 throwIfSystem()方法):
个人认为,Android 之所以增加这种访问权限限制,是基于隐私的考虑,是保证内置的 Android 应用不能访问公共存储空间,而对于非内置的应用,用户选择安装的同时,也自己承担了隐私安全的风险。
说明:以下内容来自 CSDN 作者 ID 为 echojiangyq_fight的博客文章: http://blog.csdn.net/echojiangyq_fight/article/details/28232953,感谢他给予的参考,谢谢
移植原来 v210(三星平台,Android2.3 系统)的老程序到 MTK6575 Android 4.2 上,遇到的一个问题,因为要读写 Settings 的共享数据库,必须要获得 system uid,但是这时向 sdcard 写 log 时就会遇到权限问题,陷入两者不能兼得的尴尬境地。因为有源码,选择了修改 void 从而对 system uid 开放 sdcard 写权限的方式。原来的 sdcard 权限:
root@android:/system/bin # ls -l /storage/sdcard0 ls -l /storage/sdcard0 ----rwxr-x system sdcard_rw 7390845 2012-12-15 10:43 22.mp4 d---rwxr-x system sdcard_rw 2013-05-29 00:00 Alarms d---rwxr-x system sdcard_rw 2013-05-29 00:00 Android d---rwxr-x system sdcard_rw 2013-05-29 00:00 DCIM d---rwxr-x system sdcard_rw 2013-05-29 00:00 Download d---rwxr-x system sdcard_rw 2013-05-29 00:10 LOST.DIR d---rwxr-x system sdcard_rw 2014-05-30 14:57 MTK d---rwxr-x system sdcard_rw 2013-05-29 00:00 Movies d---rwxr-x system sdcard_rw 2014-05-30 14:08 Music d---rwxr-x system sdcard_rw 2013-05-29 00:00 Notifications d---rwxr-x system sdcard_rw 2013-05-29 00:00 Pictures d---rwxr-x system sdcard_rw 2013-05-29 00:00 Podcasts d---rwxr-x system sdcard_rw 2014-05-29 19:29 Recording d---rwxr-x system sdcard_rw 2013-05-29 00:00 Ringtones d---rwxr-x system sdcard_rw 2014-05-30 17:51 TestDetailLogs d---rwxr-x system sdcard_rw 2013-05-29 01:02 mtklog d---rwxr-x system sdcard_rw 2014-05-30 17:35 test
修改源码 /system/vold/Volume.cpp
#ifdef MTK_EMMC_DISCARD
// 0702 --> 0002
if (Fat::doMount(devicePath, "mnt/secure/staging", false, false, false,
AID_SYSTEM, gid, 0002, true, IsEmmcStorage()))
{
SLOGE("%s failed to mount via VFAT (%s)\n", devicePath, strerror(errno));
continue;
}
#else //MTK_EMMC_DISCARD
// 0702 --> 0002
if (Fat::doMount(devicePath, "mnt/secure/staging", false, false, false,
AID_SYSTEM, gid, 0002, true))
{
SLOGE("%s failed to mount via VFAT (%s)\n", devicePath, strerror(errno));
continue;
}
#endif //MTK_EMMC_DISCARD
修改后编译产生新的 void 可执行文件,adb push到 /system/bin,加上可执行权限,关机重新开机,权限开放!再次查看 SD 卡读写权限:
root@android:/system/bin # ls -l /storage/sdcard0 ls -l /storage/sdcard0 -rwxrwxr-x system sdcard_rw 7390845 2012-12-15 10:43 22.mp4 drwxrwxr-x system sdcard_rw 2013-05-29 00:00 Alarms drwxrwxr-x system sdcard_rw 2013-05-29 00:00 Android drwxrwxr-x system sdcard_rw 2013-05-29 00:00 DCIM drwxrwxr-x system sdcard_rw 2013-05-29 00:00 Download drwxrwxr-x system sdcard_rw 2013-05-29 00:10 LOST.DIR drwxrwxr-x system sdcard_rw 2014-05-30 14:57 MTK drwxrwxr-x system sdcard_rw 2013-05-29 00:00 Movies drwxrwxr-x system sdcard_rw 2014-05-30 14:08 Music drwxrwxr-x system sdcard_rw 2013-05-29 00:00 Notifications drwxrwxr-x system sdcard_rw 2013-05-29 00:00 Pictures drwxrwxr-x system sdcard_rw 2013-05-29 00:00 Podcasts drwxrwxr-x system sdcard_rw 2014-05-29 19:29 Recording drwxrwxr-x system sdcard_rw 2013-05-29 00:00 Ringtones drwxrwxr-x system sdcard_rw 2014-05-30 17:51 TestDetailLogs drwxrwxr-x system sdcard_rw 2013-05-29 01:02 mtklog drwxrwxr-x system sdcard_rw 2014-05-30 17:35 test
其他: 最近在做 Bluetooth 开发中遇到一个很奇怪的问题,就是关于调用系统的提示框提示用户开启 Bluetooth ,调用的是 Activity.startActivityForResult(Intent intent, int requestCode) 方法,在 Activity.onActivityResult(int requestCode, int resultCode, Intent data) 回调方法中监听用户是否点击了确认还是取消按钮,但是发现存在用户没有点击按钮,却出现状态回调的情况,还有就是点击了按钮,却没有得到回调,最终是通过查看 Bluetooth 设置的源码,才知道原来背后的逻辑是这样的。
