最近开发一种功能,在屏幕熄灭之后,监控按键,然后发广播,启动某些应用。
FileObserver 使用过程的大坑 就是权限!两种权限,一种是SD卡的读写权限,一种是android 系统使用这个API的权限
另外,如果想在系统休眠的时候,创建文件和文件夹,需要修改 SE权限,这部分在system/sepolicy ,然后编译boot.img
system/sepolicy$ svn diff .
Index: system_server.te
===================================================================
--- system_server.te (revision 147)
+++ system_server.te (working copy)
@@ -547,9 +547,14 @@
# Do not allow opening files from external storage as unsafe ejection
# could cause the kernel to kill the system_server.
-neverallow system_server sdcard_type:dir { open read write };
-neverallow system_server sdcard_type:file rw_file_perms;
+allow system_server sdcard_type:dir { open read write add_name create_dir_perms};
+allow system_server sdcard_type:file {rw_file_perms create_file_perms rename};
+
+# avc: denied { add_name } for name="rscjakey2.txt" scontext=u:r:system_server:s0 tcontext=u:object_r:fuse:s0 tclass=dir permissive=0
+#avc: denied { write } for name="0" dev="fuse" ino=1 scontext=u:r:system_server:s0 tcontext=u:object_r:fuse:s0 tclass=dir permissive=0
+#allow system_server fuse:dir { open read write add_name };
+
# system server should never be opening zygote spawned app data
# files directly. Rather, they should always be passed via a
# file descriptor.
创建的过程是在phonewindowmanger里面的,监控按键,然后创建。
if (keyCode == 139 ) {
Log.d(TAG, "5910KeyEvent.KEYCODE_POWER=" + keyCode);
if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
Environment.setUserRequired(false);
File rscjafile = new File ("/sdcard/rscja");
File txtFile = new File ("/sdcard/rscja/key.txt");
Log.i("keyCode ssss1111 5921 ", " sssssssstxtFile.exists() ="+txtFile.exists());
if(down) {
try {
Log.i("keyCode ssss1111 5925 ", " sssssssstxtFile.exists() ="+rscjafile.exists());
if (!rscjafile.exists()) {
rscjafile.mkdirs();
Log.i("keyCode ssss1111 5928", " sssssssstxtFile.exists() ="+rscjafile.exists());
}
Log.i("keyCode ssss 5919 ", " sssssssstxtFile.exists(22222) ="+txtFile.exists());
FileOutputStream outputStream = new FileOutputStream(txtFile);
outputStream.write("1".getBytes());
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}else {
try {
Log.i("keyCode ssss 5931 ", " sssssssstxtFile.exists(22222) ="+txtFile.exists());
FileOutputStream outputStreamup = new FileOutputStream(txtFile);
outputStreamup.write("0".getBytes());
outputStreamup.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
if (repeatCountvv == 0) {
Intent myKeyIntent = new Intent();
if(down) {
myKeyIntent.setAction("com.KEY_DOWN");
}
else {
myKeyIntent.setAction("com.EY_UP");
}
myKeyIntent.putExtra("keycode", keyCode);
mContext.sendBroadcastAsUser(myKeyIntent, UserHandle.ALL);
}
return 0;
}
先贴代码
package com.chww.androidfilelistener;
import android.Manifest;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.os.FileObserver;
import android.provider.Settings;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.widget.Toast;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
public class AndroidFileListenerActivity extends Activity {
private FileObserver mFileObserver;
private FileObserver multiFileObserver;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
myRequetPermission();
if(null == mFileObserver) {
mFileObserver = new SDCardFileObserver(getApplicationContext(),Environment.getExternalStorageDirectory().getPath()+"/recja/");
mFileObserver.startWatching(); //开始监听
}
}
static class SDCardFileObserver extends FileObserver {
//mask:指定要监听的事件类型,默认为FileObserver.ALL_EVENTS
public SDCardFileObserver(String path, int mask) {
super(path, mask);
}
public SDCardFileObserver(Context ct, String path) {
super(path);
}
@Override
public void onEvent(int event, String path) {
final int action = event & FileObserver.ALL_EVENTS;
switch (action) {
case FileObserver.ACCESS:
System.out.println("event: 文件或目录被访问, path: " + path);
break;
case FileObserver.DELETE:
System.out.println("event: 文件或目录被删除, path: " + path);
break;
case FileObserver.OPEN:
System.out.println("event: 文件或目录被打开, path: " + path);
break;
case FileObserver.MODIFY:
System.out.println("event: 文件或目录被修改, path: " + path);
break;
case FileObserver.CREATE:
System.out.println("event: 文件或目录被创建, path: " + path);
break;
}
}
}
private void myRequetPermission() {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE,Manifest.permission.READ_EXTERNAL_STORAGE}, 1);
}else {
Toast.makeText(this,"您已经申请了权限!",Toast.LENGTH_SHORT).show();
}
}
private static final int NOT_NOTICE = 2;//如果勾选了不再询问
private AlertDialog alertDialog;
private AlertDialog mDialog;
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == 1) {
for (int i = 0; i < permissions.length; i++) {
if (grantResults[i] == PERMISSION_GRANTED) {//选择了“始终允许”
Toast.makeText(this, "" + "权限" + permissions[i] + "申请成功", Toast.LENGTH_SHORT).show();
} else {
if (!ActivityCompat.shouldShowRequestPermissionRationale(this, permissions[i])){//用户选择了禁止不再询问
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("permission")
.setMessage("点击允许才可以使用我们的app哦")
.setPositiveButton("去允许", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
if (mDialog != null && mDialog.isShowing()) {
mDialog.dismiss();
}
Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
Uri uri = Uri.fromParts("package", getPackageName(), null);//注意就是"package",不用改成自己的包名
intent.setData(uri);
startActivityForResult(intent, NOT_NOTICE);
}
});
mDialog = builder.create();
mDialog.setCanceledOnTouchOutside(false);
mDialog.show();
}else {//选择禁止
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("permission")
.setMessage("点击允许才可以使用我们的app哦")
.setPositiveButton("去允许", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
if (alertDialog != null && alertDialog.isShowing()) {
alertDialog.dismiss();
}
ActivityCompat.requestPermissions(AndroidFileListenerActivity.this,
new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1);
}
});
alertDialog = builder.create();
alertDialog.setCanceledOnTouchOutside(false);
alertDialog.show();
}
}
}
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if(requestCode==NOT_NOTICE){
myRequetPermission();//由于不知道是否选择了允许所以需要再次判断
}
}
}
配置文件
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.chww.androidfilelistener"
android:sharedUserId="android.uid.system">
<!-- SDCard中创建与删除文件权限 -->
<!-- 向SDCard写入数据权限 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".AndroidFileListenerActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
总结,有很多APK 是需要 android:sharedUserId="android.uid.system" 这个权限才能去做一些事情的~搞了几天才发现的。
没有任何提示啊~~!方向都找不到,只能猜
android 7.1的签名APK 方法
java -Djava.library.path=. -jar signapk.jar platform.x509.pem platform.pk8 app-debug.apk app_sign.apk
demo已经上传
怎么不能设置免积分了。