最近在做电视盒子,系统是6.0的,我负责的模块是更新模块。用的是增量更新,关于增量更新不多说,就是用阿里的工具。我负责的就是将下载的文件放到指定的目录(/cache/update.zip)下,这都是超级简单的。但更新的模块里有一个小功能就是U盘更新:就是遍历U盘的一级目录,找到指定的增量包(压缩文件),复制移动到指定目录下(/cache/update.zip)。这边的代码也超级简单,百度一大把。
4.4盒子(公司的旧盒子)一点问题没有,但6.0(公司的新盒子)就有问题,主要是读不了U盘的文件,file.listfile()==null,也就是遍历U盘的目录为空,期初以为是权限没动态获取,毕竟6.0,有些危险权限还是要尊重的。但核心论点来了。
1.我是不是可以认为,当盒子刷到系统里的时候,只要我在清单文件配置了权限,都不需要对这些权限做动态获取。
//从目前的调试结果看,我认为这一点应该是真命题。
2.是不是我动态获取了读写权限,我就可以在任意位置读写。
//我写过一个demo,运行在6.0的小米手机上,我就无法将文件写到/mnt下,但可以写到/mnt/sdcard下
这几天我遇到的问题是,当我这个app刷到系统的时候,监听到了U盘的热插拔,获得了U盘的路径,但遍历U盘的时候,listfile就是null,且就算我读取U盘的特定文件也会出错(权限有关的问题)。因为是刷到系统的,且我的清单文件也有读写权限,并且我事先在没刷盒子之前动态获取权限调试过是通过的,但一刷到系统(去除动态获取)就是有问题。所以我就认为是系统有问题,但系统那边又说他自己写过demo,在系统setting下编写从U盘copy文件到指定目录是没问题。
僵持不下,我就用了两种方案调试,
1.添加动态获取权限(读写)的代码,然后让系统打包刷机,测试是成功的但需要动态获取
2.将系统setting的权限都考到我的app清单文件下(我怀疑是少了某个权限),果不其然,测试成功。
总结:当app刷到系统目录下,是不需要动态获取权限的,但如果只有读写权限,是无法读到U盘上的文件的,至于需要的事哪个,我还没有找出,只能一锅端的全拿来。
<!-- 系统升级权限 -->
<uses-permission android:name="android.permission.REBOOT"/>
<uses-permission android:name="android.permission.RECOVERY" />
<uses-permission android:name="android.permission.ACCESS_CACHE_FILESYSTEM" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<!-- //系统设置的所有权限-->
<uses-permission android:name="android.permission.WRITE_MEDIA_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
<uses-permission android:name="android.permission.DEVICE_POWER" />
<uses-permission android:name="android.permission.CHANGE_CONFIGURATION" />
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.BLUETOOTH_PRIVILEGED" />
<uses-permission android:name="android.permission.NFC" />
<uses-permission android:name="android.permission.HARDWARE_TEST" />
<uses-permission android:name="android.permission.ACCESS_CACHE_FILESYSTEM" />
<uses-permission android:name="android.permission.CALL_PHONE" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.MASTER_CLEAR" />
<uses-permission android:name="android.permission.USE_CREDENTIALS" />
<uses-permission android:name="com.google.android.googleapps.permission.GOOGLE_AUTH" />
<uses-permission android:name="android.permission.ACCESS_DOWNLOAD_MANAGER" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.WRITE_CONTACTS" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIMAX_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIMAX_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="com.android.certinstaller.INSTALL_AS_USER" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.CLEAR_APP_USER_DATA" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.MODIFY_PHONE_STATE" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.WRITE_APN_SETTINGS"/>
<uses-permission android:name="android.permission.ACCESS_CHECKIN_PROPERTIES"/>
<uses-permission android:name="android.permission.READ_USER_DICTIONARY"/>
<uses-permission android:name="android.permission.WRITE_USER_DICTIONARY"/>
<uses-permission android:name="android.permission.FORCE_STOP_PACKAGES"/>
<uses-permission android:name="android.permission.PACKAGE_USAGE_STATS"/>
<uses-permission android:name="android.permission.BATTERY_STATS"/>
<uses-permission android:name="com.android.launcher.permission.READ_SETTINGS" />
<uses-permission android:name="com.android.launcher.permission.WRITE_SETTINGS" />
<uses-permission android:name="android.permission.MOVE_PACKAGE" />
<uses-permission android:name="android.permission.USE_CREDENTIALS" />
<uses-permission android:name="android.permission.BACKUP" />
<uses-permission android:name="android.permission.READ_SYNC_SETTINGS" />
<uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS" />
<uses-permission android:name="android.permission.READ_SYNC_STATS" />
<uses-permission android:name="android.permission.READ_SYNC_SETTINGS" />
<uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS" />
<uses-permission android:name="android.permission.STATUS_BAR" />
<uses-permission android:name="android.permission.MANAGE_USB" />
<uses-permission android:name="android.permission.SET_POINTER_SPEED" />
<uses-permission android:name="android.permission.SET_KEYBOARD_LAYOUT" />
<uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
<uses-permission android:name="android.permission.COPY_PROTECTED_DATA" />
<uses-permission android:name="android.permission.MANAGE_USERS" />
<uses-permission android:name="android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS" />
<uses-permission android:name="android.permission.READ_PROFILE" />
<uses-permission android:name="android.permission.CONFIGURE_WIFI_DISPLAY" />
<uses-permission android:name="android.permission.SET_TIME" />
<uses-permission android:name="android.permission.ACCESS_NOTIFICATIONS" />
<uses-permission android:name="android.permission.REBOOT" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.MANAGE_DEVICE_ADMINS" />
<uses-permission android:name="android.permission.READ_SEARCH_INDEXABLES" />
<uses-permission android:name="android.permission.OEM_UNLOCK_STATE" />
<uses-permission android:name="android.permission.OVERRIDE_WIFI_CONFIG" />
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
<uses-permission android:name="android.permission.MANAGE_FINGERPRINT" />
<uses-permission android:name="android.permission.USER_ACTIVITY" />
<uses-permission android:name="android.permission.CHANGE_APP_IDLE_STATE" />
<uses-permission android:name="android.permission.PEERS_MAC_ADDRESS"/>
也许是这个盒子的6.0系统的原因,因为这个6.0版本是方案商公司的第一个6.0版本(又当小白鼠),也许是所有6.0以上都会有这个问题,今天积累下这个经验,让别人少走些弯路。
附件:U盘更新工具类
package com.familybox.model.businessImpl;
import android.Manifest;
import android.app.Activity;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.support.v4.app.ActivityCompat;
import android.util.Log;
import com.familybox.utils.Constants;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.concurrent.TimeUnit;
import io.reactivex.Flowable;
import io.reactivex.schedulers.Schedulers;
/**
* @创建人:hcy
* @创建时间:2018/5/31
* @作用描述:Function:usb 更新工具类
**/
public class UsbUpdateUtils {
private Context context;
public UsbUpdateUtils(Context context) {
this.context = context;
}
public void usb_update(String current_apk_name) {
//读取当前USB的路径
SharedPreferences mSharedPreferences = context.getSharedPreferences("setting", Context.MODE_PRIVATE);
String current_usb_path = mSharedPreferences.getString("usb_path", "none");
//获取根目录
File usbFile = new File(current_usb_path);
if (usbFile != null && usbFile.exists()) {
Log.i("UpdateApk>>>", "当前有U盘" + current_usb_path);
Log.i("UpdateApk>>>", "开始遍历U盘的一级目录");
if (usbUpdateListener != null) {
usbUpdateListener.testtishi("当前有U盘" + current_usb_path);
usbUpdateListener.testtishi("\n" + "开始遍历U盘的一级目录");
}
//查找更新文件
File[] files = usbFile.listFiles();
if (files != null) {
for (int i = 0; i < files.length; i++) {
Log.i("UpdateApk>>>", "U盘文件名:" + files[i].getAbsolutePath() + "???" + files[i].getName());
if (usbUpdateListener != null) {
usbUpdateListener.testtishi("\n" + "U盘文件名:" + files[i].getAbsolutePath() + "???" + files[i].getName());
}
File file = files[i];
if (file.getName().contains("update.zip")) {
Log.i("UpdateApk>?>", "找到要跟新的增量包:" + files[i].getName());
Log.i("UpdateApk>?>", "开始复制到指定目录:" + Constants.DOWN_FILE_DIR + "/" + Constants.UPDATE_FILE_NAME);
if (usbUpdateListener != null) {
Log.i("UpdateApk>>>", "找到要跟新的增量包:" + files[i].getName());
Log.i("UpdateApk>>>", "开始复制到指定目录");
usbUpdateListener.testtishi("\n" + "找到要跟新的增量包:" + files[i].getName());
usbUpdateListener.testtishi("\n" + "开始复制到指定目录");
}
Flowable.timer(2, TimeUnit.SECONDS)
.subscribeOn(Schedulers.io())
.subscribe((tag) -> {
copyFileToSystem(file);
});
break;
}
}
} else {
Log.i("UpdateApk>>>", "获取不到U盘的文件usbFile.listFiles()==null");
if (usbUpdateListener != null) {
usbUpdateListener.testtishi("\n" + "获取不到U盘的文件usbFile.listFiles()==null");
usbUpdateListener.testtishi("\n" + "开始极端处理");
usbUpdateListener.testtishi("\n" + "获取" + current_usb_path + "/update.zip" + "文件");
}
File file = new File(current_usb_path + "/update.zip");
if (file != null && file.exists()) {
if (usbUpdateListener != null) {
usbUpdateListener.testtishi("\n" + "该文件存在2秒后复制:" + current_usb_path + "/update.zip");
}
Flowable.timer(2, TimeUnit.SECONDS)
.subscribeOn(Schedulers.io())
.subscribe((tag) -> {
copyFileToSystem(file);
});
} else {
if (usbUpdateListener != null) {
usbUpdateListener.testtishi("\n" + "文件不存在:" + current_usb_path + "/update.zip");
}
}
}
} else {
Log.i("UpdateApk>>>", "当前没有U盘" + current_usb_path);
if (usbUpdateListener != null) {
usbUpdateListener.usb_exist(false);
usbUpdateListener.testtishi("当前没有U盘" + current_usb_path);
}
}
/* SharedPreferences mSharedPreferences = context.getSharedPreferences("setting", Context.MODE_PRIVATE);
String current_usb_path = mSharedPreferences.getString("usb_path", "none");
if (usbFile != null && usbFile.exists()) {
Log.i("UpdateApk>>>", "当前有U盘" + current_usb_path);
Log.i("UpdateApk>>>", "开始遍历U盘的一级目录");
if (usbUpdateListener != null) {
usbUpdateListener.testtishi("当前有U盘" + current_usb_path);
usbUpdateListener.testtishi("\n" + "开始遍历U盘的一级目录");
}
File[] files = usbFile.listFiles();
for (int i = 0; i < files.length; i++) {
Log.i("UpdateApk>>>", "U盘文件名:" + files[i].getAbsolutePath() + "???" + files[i].getName());
if (usbUpdateListener != null) {
usbUpdateListener.testtishi("\n" + "U盘文件名:" + files[i].getAbsolutePath() + "???" + files[i].getName());
}
// tv_tishi.setText(tv_tishi.getText() + "\n" + "U盘文件名:" + files[i].getAbsolutePath() + "???" + files[i].getName());
File file = files[i];
if (file.getName().contains("update.zip")) {
Log.i("UpdateApk>?>", "找到要跟新的增量包:" + files[i].getName());
Log.i("UpdateApk>?>", "开始复制到指定目录:" + Constants.UPDATE_APK_FILE_PATH);
if (usbUpdateListener != null) {
Log.i("UpdateApk>>>", "找到要跟新的增量包:" + files[i].getName());
Log.i("UpdateApk>>>", "开始复制到指定目录");
usbUpdateListener.testtishi("\n" + "找到要跟新的增量包:" + files[i].getName());
usbUpdateListener.testtishi("\n" + "开始复制到指定目录");
}
// tv_tishi.setText(tv_tishi.getText() + "\n" + "找到要跟新的增量包:" + files[i].getName());
// tv_tishi.setText(tv_tishi.getText() + "\n" + "开始复制到指定目录");
// tag = tv_tishi.getText().toString();
Flowable.timer(2, TimeUnit.SECONDS)
.subscribeOn(Schedulers.io())
.subscribe((tag) -> {
copyFileToSystem(file);
});
break;
}
}
} else {
if (usbUpdateListener != null) {
usbUpdateListener.usb_exist(false);
usbUpdateListener.testtishi("当前没有U盘" + current_usb_path);
}
}*/
/* File file = new File(current_usb_path + "/update.zip");
if (file != null && file.exists()) {
Log.i("eee", "XXXX");
Flowable.timer(2, TimeUnit.SECONDS)
.subscribeOn(Schedulers.io())
.subscribe((tag) -> {
copyFileToSystem(file);
});
} else {
Log.i("eee", "x2XXXX");
}*/
}
/**
* 复制文件的操作
*
* @param needCopyFile
*/
private void copyFileToSystem(File needCopyFile) {
InputStream inStream = null;
FileOutputStream fs = null;
try {
int bytesum = 0;
int byteread = 0;
if (needCopyFile.exists()) { //需要复制的文件存在时
inStream = new FileInputStream(needCopyFile.getAbsolutePath()); //读入原文件
File targetDir = new File(Constants.DOWN_FILE_DIR + "/" + Constants.UPDATE_FILE_NAME);//目标文件
Log.i("UpdateApk>>>", "复制单个文件" + targetDir.getAbsolutePath());
/* if (targetDir != null && !targetDir.exists()) {
targetDir.mkdirs();
}*/
targetDir.createNewFile();
fs = new FileOutputStream(targetDir);
// fs = new FileOutputStream(Constants.DOWN_FILE_DIR + "/" + Constants.UPDATE_FILE_NAME);
byte[] buffer = new byte[1024];
while ((byteread = inStream.read(buffer)) != -1) {
bytesum += byteread; //字节数 文件大小
fs.write(buffer, 0, byteread);
if (usbUpdateListener != null) {
usbUpdateListener.usb_update_progress(bytesum, needCopyFile.length());
}
}
fs.flush();
}
} catch (Exception e) {
Log.i("UpdateApk>>>", "复制单个文件操作出错" + e.getMessage());
if (usbUpdateListener != null) {
usbUpdateListener.copyAndMoveFailed("复制单个文件操作出错" + e.getMessage());
usbUpdateListener.testtishi("\n" + "复制单个文件操作出错" + e.getMessage());
}
Log.i("复制单个文件操作出错", "复制单个文件操作出错" + e.getMessage());
e.printStackTrace();
} finally {
if (inStream != null) {
try {
inStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (fs != null) {
try {
fs.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public interface UsbUpdateListener {
void usb_exist(boolean exist);
void testtishi(String msg);
void usb_update_progress(long current, long total);
void copyAndMoveFailed(String msg);
}
private UsbUpdateListener usbUpdateListener;
public void setUsbUpdateListener(UsbUpdateListener usbUpdateListener) {
this.usbUpdateListener = usbUpdateListener;
}
private static final int REQUEST_EXTERNAL_STORAGE = 1;
private static String[] PERMISSIONS_STORAGE = {
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE};
public static void verifyStoragePermissions(Activity activity) {
// Check if we have write permission
int permission = ActivityCompat.checkSelfPermission(activity,
Manifest.permission.WRITE_EXTERNAL_STORAGE);
if (permission != PackageManager.PERMISSION_GRANTED) {
// We don't have permission so prompt the user
ActivityCompat.requestPermissions(activity, PERMISSIONS_STORAGE,
REQUEST_EXTERNAL_STORAGE);
}
}
}
不好意思,扯了一堆废话,最后还是找到了原因。之所以/cache下写不进去,只要还是这个目录不具备相应写入权限。我看公司同事使用linux指令 chmod 777 打开这个权限就ok了。