观心静

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

前言

  此篇博客只展示一些系统级别应用的开发功能记录,这些功能都需要系统签名系统级权限

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.xxxx.xxxx"
    android:sharedUserId="android.uid.system">

普通应用无法调用这些功能!如果有初学者看到这篇博客请一定区分自己开发的应用是系统应用还是普通应用。普通应用就不需要浪费时间到这里了。

另外,下面的代码其实大部分都是调用framework里的代码,但是是以反射的形式调用,如果你手上有一份framework可以帮助你理解代码的上下文

将应用设置成Launcher应用

清单文件中应该注意的事情

注意,要添加singleTask,否则会出现home键多次创建launchar 应用

  <application>
        <activity
            android:name=".ui.MainActivity"
            android:exported="true"
            android:launchMode="singleTask">
            <intent-filter>
                <!--  隐藏图标   -->
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
                <!-- 设置为 launcher -->
                <category android:name="android.intent.category.HOME" />
                <category android:name="android.intent.category.DEFAULT" />
                <category android:name="android.intent.category.rayland.home" />
            </intent-filter>
        </activity>
    </application>

屏蔽一些按键防止launchar受影响

    override fun onKeyUp(keyCode: Int, event: KeyEvent?): Boolean {
        if (keyCode == KeyEvent.KEYCODE_BACK) {
            return true
        }
        if (keyCode == KeyEvent.KEYCODE_MENU) {
            return true
        }
        if (keyCode == KeyEvent.KEYCODE_HOME) {
            return true
        }
}

启动桌面应用的方式

    /**
     * 启动应用
     */
    fun startApp(packageName: String) {
        try {
            this@MainActivity.packageManager.getLaunchIntentForPackage(packageName)?.let { intent ->
                startActivity(intent)
            }
        } catch (e: Exception) {
            e.printStackTrace()
            Toast.makeText(this, "应用启动失败", Toast.LENGTH_SHORT).show()
        }
    }

    /**
     * 启动应用
     * [packageName] 包名
     * [className] 类名(请注意需要路径+类名)
     */
    fun startApp(packageName: String, className: String) {
        try {
            startActivity(Intent().apply {
                setClassName(packageName, className)
            })
        } catch (e: Exception) {
            e.printStackTrace()
            Log.e("zh", "应用启动失败 : $e" )
            Toast.makeText(this, "应用启动失败", Toast.LENGTH_SHORT).show()
        }
    }

判断应用或者服务是否启动

class AppUtil {

    companion object{

        /**
         * 判断应用是否已经启动
         *
         * @param context   上下文对象
         * @param packageName 要判断应用的包名
         * @return boolean
         */
        private fun isAppAlive(context: Context, packageName: String): Boolean {
            val activityManager = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
            val processInfos = activityManager.runningAppProcesses
            if (!processInfos.isNullOrEmpty()) {
                for (item in processInfos) {
                    if (item.processName == packageName) {
                        return true
                    }

                }
            }
            return false
        }

        /**
         * 判断服务是否开启
         *
         * @return
         */
        fun isServiceRunning(context: Context, ServiceName: String?): Boolean {
            if (TextUtils.isEmpty(ServiceName)) {
                return false
            }
            val myManager = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
            val runningService = myManager.getRunningServices(1000) as List<ActivityManager.RunningServiceInfo>
            for (i in runningService.indices) {
                Log.i("服务运行1:", "" + runningService[i].service.className.toString())
                if (runningService[i].service.className.toString().equals(ServiceName)) {
                    return true
                }
            }
            return false
        }
    }
}

获取系统中安装的应用列表

bean

data class ApplyBean(val name: String?, val icon: Drawable?, val packageName: String?)

获取数据

    private fun getAllApps(): List<ApplyBean> {
        val packageInfoArray = mutableListOf<PackageInfo>()
        val applyBeanArray = mutableListOf<ApplyBean>()
        val pManager = MainApp.getApp().packageManager

        // 获取手机内所有应用
        val packlist = pManager.getInstalledPackages(0)
        for (i in packlist.indices) {
            val pak = packlist[i] as PackageInfo
            // if()里的值如果<=0则为自己装的程序,否则为系统工程自带
            if (pak.applicationInfo.flags and ApplicationInfo.FLAG_SYSTEM <= 0) {
                // 添加自己已经安装的应用程序
                packageInfoArray.add(pak)
            } else {
                //apps.add(pak)
            }
        }
        for (pinfo in packageInfoArray) {
            //设置图片
            val icon = pManager.getApplicationIcon(pinfo.applicationInfo)
            //设置应用程序名字
            val appName = pManager.getApplicationLabel(pinfo.applicationInfo).toString()
            //设置应用程序的包名
            val packageName = pinfo.applicationInfo.packageName
            val app = ApplyBean(appName, icon, packageName)
            applyBeanArray.add(app)
        }
        return applyBeanArray
    }

应用变化监听

    inner class AppUpdateReceiver : BroadcastReceiver() {
        override fun onReceive(context: Context, intent: Intent) {

            if (intent.getAction().equals(Intent.ACTION_PACKAGE_ADDED)) {
                var packageName = intent.getDataString()
                Log.e(TAG, packageName + "安装成功")
            } else if (intent.getAction().equals(Intent.ACTION_PACKAGE_REPLACED)) {
                var packageName = intent.getData().getSchemeSpecificPart()
                Log.e(TAG, packageName + "替换成功")
            } else if (intent.getAction().equals(Intent.ACTION_PACKAGE_REMOVED)) {
                var packageName = intent . getData ().getSchemeSpecificPart()
                Log.e(TAG, packageName + "卸载成功")
            }

        }
    }


public static void registerReceiver(Context context) {
        mIntentFilter = new IntentFilter();
        mIntentFilter.addDataScheme("package");
        mIntentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
        mIntentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
        mIntentFilter.addAction(Intent.ACTION_PACKAGE_REPLACED);
        context.registerReceiver(mReceiver, mIntentFilter);
    }

获取应用版本Code与版本名称

fun getVersionsCode(context: Context,packageName:String):Int{
    return context.packageManager.getPackageInfo(packageName,0).versionCode
}

fun getVersionsName(context: Context,packageName:String):String{
    return context.packageManager.getPackageInfo(packageName,0).versionName
}

静默安装应用

import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
import android.os.Build;
import android.util.Log;

import androidx.annotation.RequiresApi;

import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;


/**
    app静默安装工具类
 */
public class InstallUpdateUtil {

    private static final String TAG = InstallUpdateUtil.class.getSimpleName();

    // 适配android9的安装方法。
    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    public static void install28(Context context, String apkFilePath, PackageManager packageManager) {
        File apkFile = new File(apkFilePath);
        PackageInstaller packageInstaller = packageManager.getPackageInstaller();
        PackageInstaller.SessionParams sessionParams = new PackageInstaller.SessionParams(PackageInstaller.SessionParams.MODE_FULL_INSTALL);
        sessionParams.setSize(apkFile.length());

        int sessionId = createSession(packageInstaller, sessionParams);
        if (sessionId != -1) {
            boolean copySuccess = copyInstallFile(packageInstaller, sessionId, apkFilePath);
            if (copySuccess) {
                execInstallCommand(context, packageInstaller, sessionId);
            }
        }
    }

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    private static int createSession(PackageInstaller packageInstaller,
                              PackageInstaller.SessionParams sessionParams) {
        int sessionId = -1;
        try {
            sessionId = packageInstaller.createSession(sessionParams);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return sessionId;
    }

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    private static boolean copyInstallFile(PackageInstaller packageInstaller,
                                    int sessionId, String apkFilePath) {
        InputStream in = null;
        OutputStream out = null;
        PackageInstaller.Session session = null;
        boolean success = false;
        try {
            File apkFile = new File(apkFilePath);
            session = packageInstaller.openSession(sessionId);
            out = session.openWrite("base.apk", 0, apkFile.length());
            in = new FileInputStream(apkFile);
            int total = 0, c;
            byte[] buffer = new byte[65536];
            while ((c = in.read(buffer)) != -1) {
                total += c;
                out.write(buffer, 0, c);
            }
            session.fsync(out);
            Log.i(TAG, "streamed " + total + " bytes");
            success = true;
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            closeQuietly(out);
            closeQuietly(in);
            closeQuietly(session);
        }
        return success;
    }

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    private static void execInstallCommand(Context context, PackageInstaller packageInstaller, int sessionId) {
        PackageInstaller.Session session = null;
        try {
            session = packageInstaller.openSession(sessionId);
            Intent intent = new Intent(context, InstallResultReceiver.class);
            PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 1, intent, PendingIntent.FLAG_UPDATE_CURRENT);
            session.commit(pendingIntent.getIntentSender());
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            closeQuietly(session);
        }
    }


    public static class InstallResultReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            if (intent != null) {
                final int status = intent.getIntExtra(PackageInstaller.EXTRA_STATUS,
                        PackageInstaller.STATUS_FAILURE);
                if (status == PackageInstaller.STATUS_SUCCESS) {
                    // success
                } else {
                    //Log.e(TAG, intent.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE));
                }
            }
        }
    }

    public static void closeQuietly(Closeable c) {
        if (c != null) {
            try {
                c.close();
            } catch (IOException ignored) {
                ignored.printStackTrace();
            }
        }
    }

    public static void closeQuietly(Socket c) {
        if (c != null) {
            try {
                c.close();
            } catch (IOException ignored) {
                ignored.printStackTrace();
            }
        }
    }
}

卸载应用

这种方式卸载应用会有弹窗提示,让用户操作选择是否卸载

需要系统级权限

    <!--  卸载应用权限 系统级权限-->
    <uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES" />
    <uses-permission android:name="android.permission.DELETE_PACKAGES" />

代码

不要吃惊,没错卸载是依靠一个系统activity实现的

    /**
     * 卸载应用
     */
    fun uninstallApp(packageName: String) {
        try {
            val i: Intent = Intent.parseUri("#Intent;action=android.intent.action.DELETE;launchFlags=0x10800000;end", 0)
                .setData(Uri.fromParts("package", packageName, null))
                .putExtra(Intent.EXTRA_USER, Process.myUserHandle())
            this.startActivity(i)
        } catch (e: URISyntaxException) {
            Log.e("lwlx", "Failed to parse intent to start uninstall activity for item=$e")
        }
    }

上面卸载的来源,luancher3源码

    /**
     * Performs the drop action and returns the target component for the dragObject or null if
     * the action was not performed.
     */
    protected ComponentName performDropAction(View view, ItemInfo info) {
        if (mCurrentAccessibilityAction == RECONFIGURE) {
            int widgetId = getReconfigurableWidgetId(view);
            if (widgetId != INVALID_APPWIDGET_ID) {
                mLauncher.getAppWidgetHost().startConfigActivity(mLauncher, widgetId, -1);
            }
            return null;
        }
        // else: mCurrentAccessibilityAction == UNINSTALL

        ComponentName cn = getUninstallTarget(info);
        if (cn == null) {
            // System applications cannot be installed. For now, show a toast explaining that.
            // We may give them the option of disabling apps this way.
            Toast.makeText(mLauncher, R.string.uninstall_system_app_text, Toast.LENGTH_SHORT).show();
            return null;
        }
        try {
            Intent i = Intent.parseUri(mLauncher.getString(R.string.delete_package_intent), 0)
                    .setData(Uri.fromParts("package", cn.getPackageName(), cn.getClassName()))
                    .putExtra(Intent.EXTRA_USER, info.user);
            mLauncher.startActivity(i);
            return cn;
        } catch (URISyntaxException e) {
            Log.e(TAG, "Failed to parse intent to start uninstall activity for item=" + info);
            return null;
        }
    }

静默卸载应用

//静默卸载
fun silentUninstallPackage(context: Context, packageName: String) {
    val jh: Long = Binder.clearCallingIdentity()
    val observer = object : IPackageDeleteObserver {
        override fun asBinder(): IBinder? {
            return null
        }

        override fun packageDeleted(p0: String?, p1: Int) {
            Log.e("zh", "packageDeleted:${p0} ${p1}")
        }
    }
    val packageManager = context.packageManager
    //deletePackage方法无法直接调用,这里直接反射
    val deletePackage = packageManager.javaClass.getMethod("deletePackage", String::class.java, IPackageDeleteObserver::class.java, Int::class.java)
    /*
        下面的0x00000002来源在PackageManager类的常量DELETE_ALL_USERS
        //  Flag parameter for {@link #deletePackage} to indicate that you want the package deleted for all users.
        public static final int DELETE_ALL_USERS = 0x00000002;
     */
    deletePackage.invoke(packageManager, packageName, observer, 0x00000002)
    Binder.restoreCallingIdentity(jh)
}

强制停止应用

import android.app.ActivityManager
import android.content.Context
import java.lang.reflect.Method

class AppRunUtil {
    companion object {
        /**
         * 强制停止[pkgName]应用程序
         * @param pkgName
         */
        fun forceStopPackage(context: Context, pkgName: String) {
            val am = context?.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
            val method: Method = Class.forName("android.app.ActivityManager").getMethod("forceStopPackage", String::class.java)
            method.invoke(am, pkgName)
        }

        /**
         * 目标[pkgName]是否在运行
         */
        fun isAppRunning(context: Context, pkgName: String): Boolean {
            val am = context?.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
            val list = am.getRunningTasks(100)
            var isAppRunning = false

            for (info in list) {
                if (info.topActivity.packageName == pkgName && info.baseActivity.packageName == pkgName) {
                    isAppRunning = true
                    break
                }
            }
            return isAppRunning
        }
    }
}

系统时间设置

需要系统级权限

<uses-permission android:name="android.permission.SET_TIME"/>
<uses-permission android:name="android.permission.SET_TIME_ZONE"/>
<uses-permission android:name="android.permission.WRITE_SETTINGS"/>
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS"/>

代码

        /**
         * 设置时间
         */
        fun setTime(context: Context, millis: Long) {
            val am: AlarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
            am.setTime(millis)
        }

        /**
         * 是否是24小时格式
         * @return true=24小时 false=12小时
         */
        fun is24HourMode(context: Context): Boolean {
            return Settings.System.getString(context.contentResolver, Settings.System.TIME_12_24) == "24"
        }

        /**
         * 设置时间格式
         * [is24HourMode] true=24小时 false=12小时
         */
        fun setHourMode(context: Context, is24HourMode: Boolean) {
            Settings.System.putString(context.contentResolver, Settings.System.TIME_12_24, if (is24HourMode) "24" else "12")
        }

        /**
         * 是否是自动校时
         */
        fun isAutoTime(context: Context) =
            Settings.Global.getInt(context.contentResolver, Settings.Global.AUTO_TIME, 0) == 1

        /**
         * 设置自动校时
         * [enable] 1=启用 0=禁用
         */
        fun setAutoTime(context: Context, enable: Int) {
            Settings.Global.putInt(context.contentResolver, Settings.Global.AUTO_TIME, enable)
        }

系统存储各类信息获取

/**
 * 权限
 *   android.permission.PACKAGE_USAGE_STATS
 */
class StorageViewModel(application: Application) : AndroidViewModel(application) {
    private val mContentResolver by lazy { getApplication<Application>().contentResolver }

    private val _typeStorageSize = SingleLiveEvent<List<Pair<TypeStorage, Long>>>()
    val typeStorageSize: LiveData<List<Pair<TypeStorage, Long>>> get() = _typeStorageSize

    /**
     * 总共空间
     */
    private val _totalStorageSize = SingleLiveEvent<Long>()
    val totalStorageSize: LiveData<Long> get() = _totalStorageSize

    /**
     * 可用空间
     */
    private val _availableStorageSize = SingleLiveEvent<Long>()
    val availableStorageSize: LiveData<Long> get() = _availableStorageSize

    /**
     * 初始化存储大小
     */
    @RequiresApi(Build.VERSION_CODES.O)
    fun initStorageSize() {
        viewModelScope.launch(Dispatchers.Default) {
            _totalStorageSize.postValue(getTotalSize())
            _availableStorageSize.postValue(getAvailableSize())
            val list = mutableListOf<Pair<TypeStorage, Long>>()
            list.add(TypeStorage.MUSICS to getMusicsStorageSize())
            list.add(TypeStorage.IMAGE to getImageStorageSize())
            list.add(TypeStorage.VIDEO to getVideoStorageSize())
            list.add(TypeStorage.OTHER to getOtherStorageSize())
            list.add(TypeStorage.APP to getAppsStorageSize())
            _typeStorageSize.postValue(list)
        }
    }

    /**
     * 获取全部应用存储大小
     */
    @RequiresApi(Build.VERSION_CODES.O)
    private suspend fun getAppsStorageSize(): Long {
        val pManager = getApplication<Application>().packageManager
        var totalSize = 0L
        // 获取手机内所有应用
        val packlist = pManager.getInstalledPackages(0)
        for (i in packlist.indices) {
            val pak = packlist[i] as PackageInfo
            val size = getAppStorageSize(pak.packageName)
            totalSize += size
        }
        return totalSize
    }

    /**
     * 获取单个应用存储大小
     */
    @RequiresApi(Build.VERSION_CODES.O)
    private suspend fun getAppStorageSize(packageName: String): Long {
        val mStorageStatsManager: StorageStatsManager = getApplication<Application>().getSystemService(Context.STORAGE_STATS_SERVICE) as StorageStatsManager
        val mStorageManager: StorageManager = getApplication<Application>().getSystemService(Context.STORAGE_SERVICE) as StorageManager
        val storageVolumes = mStorageManager.storageVolumes
        var appSize = 0L
        for (item in storageVolumes) {
            val uuid: UUID = if (item.uuid.isNullOrEmpty()) {
                StorageManager.UUID_DEFAULT
            } else {
                try {
                    UUID.fromString(item.uuid)
                } catch (e: IllegalArgumentException) {
                    StorageManager.UUID_DEFAULT
                }
            }
            val userHandle: UserHandle = Process.myUserHandle()
            val stats: StorageStats = mStorageStatsManager.queryStatsForPackage(uuid, packageName, userHandle)
            appSize = stats.appBytes + stats.cacheBytes + stats.dataBytes
        }
        return appSize
    }

    /**
     * 获取音乐存储大小
     * @return
     */
    private suspend fun getMusicsStorageSize(): Long {
        var totalSize = 0L
        val proj = arrayOf(MediaStore.Video.Media.SIZE, MediaStore.Video.Media.DATA)
        var cursor: Cursor? = mContentResolver?.query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, proj, null, null, null)
        if (cursor != null) {
            while (cursor.moveToNext()) {
                val path: String = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.DATA)) // 路径
                val size: Long = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.SIZE)) // 大小
                totalSize += size
            }
            cursor.close()
        }
        return totalSize
    }


    /**
     * 得到图片存储大小
     * https://cloud.tencent.com/developer/article/1489208
     */
    private suspend fun getImageStorageSize(): Long {
        var totalSize = 0L
        val projImage = arrayOf(
            MediaStore.Images.Media._ID, MediaStore.Images.Media.DATA, MediaStore.Images.Media.SIZE
        )
        val cursor = mContentResolver.query(
            MediaStore.Images.Media.EXTERNAL_CONTENT_URI, projImage,
            MediaStore.Images.Media.MIME_TYPE + "= ? or " + MediaStore.Images.Media.MIME_TYPE + "= ?", arrayOf("image/jpeg", "image/png"), MediaStore.Images.Media.DATE_MODIFIED
        )
        if (cursor != null) {
            while (cursor.moveToNext()) {
                val path = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA))
                val size = cursor.getLong(cursor.getColumnIndex(MediaStore.Images.Media.SIZE))
                totalSize += size
            }
            cursor.close()
        }
        return totalSize
    }

    /**
     *  获取视频存储大小
     */
    @SuppressLint("Recycle")
    private suspend fun getVideoStorageSize(): Long {
        var totalSize = 0L
        val video = arrayOf(
            MediaStore.Video.Media._ID, MediaStore.Video.Media.DATA, MediaStore.Video.Media.SIZE, MediaStore.Video.Media.DISPLAY_NAME
        )
        val cursor = mContentResolver.query(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, video, null, null, null)
        if (cursor != null) {
            while (cursor.moveToNext()) {
                val path = cursor.getString(cursor.getColumnIndex(MediaStore.Video.Media.DATA))
                val size = cursor.getLong(cursor.getColumnIndex(MediaStore.Video.Media.SIZE))
                totalSize += size
            }
            cursor.close()
        }
        return totalSize
    }

    /**
     * 其他文件存储
     */
    private suspend fun getOtherStorageSize(): Long {
        val projection = arrayOf(MediaStore.Files.FileColumns._ID, MediaStore.Files.FileColumns.DATA, MediaStore.Files.FileColumns.TITLE, MediaStore.Files.FileColumns.MIME_TYPE, MediaStore.Files.FileColumns.SIZE)
        val cursor = mContentResolver.query(MediaStore.Files.getContentUri("external"), projection, null, null, MediaStore.Files.FileColumns.DATE_MODIFIED + " desc")
        var totalSize = 0L
        if (cursor != null) {
            while (cursor.moveToNext()) {
                val path = cursor.getString(cursor.getColumnIndex(MediaStore.Files.FileColumns.DATA)) // 路径
                val size = cursor.getLong(cursor.getColumnIndex(MediaStore.Files.FileColumns.SIZE))
                val mimeType = cursor.getString(cursor.getColumnIndex(MediaStore.Files.FileColumns.MIME_TYPE))
                if (mimeType == "image/png" || mimeType == "video/mp4" || mimeType == "audio/mpeg") {
                    //排除图片、视频、音频文件
                    continue
                }
                totalSize += size
            }
            cursor.close()
        }
        return totalSize
    }

    /**
     * SD卡大小
     */
    fun readSDCard() {
        val state = Environment.getExternalStorageState()
        if (Environment.MEDIA_MOUNTED == state) {
            val sdcardDir = Environment.getExternalStorageDirectory()
            val sf = StatFs(sdcardDir.path)
            val blockSize = sf.blockSize.toLong()
            val blockCount = sf.blockCount.toLong()
            val availCount = sf.availableBlocks.toLong()
            Log.d("ytzn", "block大小:" + blockSize + ",block数目:" + blockCount + ",总大小:" + blockSize * blockCount / 1024 + "KB")
            Log.d("ytzn", "可用的block数目::" + availCount + ",剩余空间:" + availCount * blockSize / 1024 + "KB")
        }
    }

    /**
     * 获取总空间大小
     *
     * @return 单位KB
     */
    private suspend fun getTotalSize(): Long {
        val state = Environment.getExternalStorageState()
        if (Environment.MEDIA_MOUNTED == state) {
            val sdcardDir = Environment.getExternalStorageDirectory()
            val sf = StatFs(sdcardDir.path)
            val blockSize = sf.blockSizeLong
            val blockCount = sf.blockCountLong
            return blockSize * blockCount / 1024
        }
        return 0
    }

    /**
     * 剩余空间大小
     *
     * @return 单位KB
     */
    private suspend fun getAvailableSize(): Long {
        val state = Environment.getExternalStorageState()
        if (Environment.MEDIA_MOUNTED == state) {
            val sdcardDir = Environment.getExternalStorageDirectory()
            val sf = StatFs(sdcardDir.path)
            val blockSize = sf.blockSizeLong
            val availCount = sf.availableBlocksLong
            return availCount * blockSize / 1024
        }
        return 0
    }
}

public enum class TypeStorage {
    APP, MUSICS, IMAGE, VIDEO, OTHER
}

清理应用数据

可以作为垃圾清理功能使用

import android.app.ActivityManager
import android.content.Context
import android.content.pm.PackageManager
import java.lang.reflect.InvocationTargetException

/**
 * 清理垃圾工具累
 */
class ClearAppDataUtil {

    companion object {

        /**
         * 深度清理应用垃圾,此方法会让应用的用户数据也被清除
         */
        fun clearData(packageName: String, context: Context) {
            val am = context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
            val stubClass = Class.forName("android.content.pm.IPackageDataObserver")
            val method = ActivityManager::class.java.getDeclaredMethod("clearApplicationUserData", String::class.java, stubClass)
            val a = method.invoke(am, packageName, null) as Boolean
        }

        /**
         * 深度清理应用垃圾,此方法会让应用的用户数据也被清除,这个方法与上面的clearData功能一样,只不过这是从PackageManager清理,
         * 看framework代码都是没区别的,还不知道它们在其他细节上有什么区别
         */
        fun clearUserData(packageName: String, context: Context) {
            val packageManager = context.packageManager
            try {
                val stubClass = Class.forName("android.content.pm.IPackageDataObserver")
                val method = PackageManager::class.java.getDeclaredMethod("clearApplicationUserData", String::class.java, stubClass)
                method.invoke(packageManager, packageName, null)
            } catch (e: NoSuchMethodException) {
                e.printStackTrace()
            } catch (e: IllegalAccessException) {
                e.printStackTrace()
            } catch (e: InvocationTargetException) {
                e.printStackTrace()
            }
        }

        /**
         * 清理应用缓存垃圾,此方法只清理cache文件夹中的缓存数据
         */
        fun clearCache(packageName: String, context: Context) {
            val packageManager = context.packageManager
            try {
                val stubClass = Class.forName("android.content.pm.IPackageDataObserver")
                val method = PackageManager::class.java.getDeclaredMethod("deleteApplicationCacheFiles", String::class.java, stubClass)
                method.invoke(packageManager, packageName, null)
            } catch (e: NoSuchMethodException) {
                e.printStackTrace()
            } catch (e: IllegalAccessException) {
                e.printStackTrace()
            } catch (e: InvocationTargetException) {
                e.printStackTrace()
            }
        }
    }
}

系统亮度设置

需要系统级权限

<uses-permission android:name="android.permission.WRITE_SETTINGS"/>
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS"/>

代码

        /**
         * 设置亮度
         */
        fun setBrightness(context: Context, brightness: Int) {
            Settings.System.putInt(context.contentResolver, Settings.System.SCREEN_BRIGHTNESS_MODE, Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL)
            Settings.System.putInt(context.contentResolver, Settings.System.SCREEN_BRIGHTNESS, brightness)
        }

        /**
         * 获取亮度
         */
        fun getBrightness(context: Context) =
            Settings.System.getInt(context.contentResolver, Settings.System.SCREEN_BRIGHTNESS)

系统音量设置

需要系统级权限

<uses-permission android:name="android.permission.WRITE_SETTINGS"/>
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS"/>

代码

        /**
         * 获取多媒体的声音音量
         *
         * STREAM_VOICE_CALL 通话
         * STREAM_SYSTEM 系统
         * STREAM_RING 铃声
         * STREAM_MUSIC 媒体音量
         * STREAM_ALARM 闹钟
         * STREAM_NOTIFICATION 通知
         */
        fun getMusicSound(context: Context) =
            (context.getSystemService(Context.AUDIO_SERVICE) as AudioManager).getStreamVolume(AudioManager.STREAM_MUSIC)

        /**
         * 获取多媒体的最大声音音量
         *
         * STREAM_VOICE_CALL 通话
         * STREAM_SYSTEM 系统
         * STREAM_RING 铃声
         * STREAM_MUSIC 媒体音量
         * STREAM_ALARM 闹钟
         * STREAM_NOTIFICATION 通知
         */
        fun getMaxMusicSound(context: Context) =
            (context.getSystemService(Context.AUDIO_SERVICE) as AudioManager).getStreamMaxVolume(AudioManager.STREAM_MUSIC)

        /**
         * 设置多媒体的声音音量
         */
        fun setMusicSound(context: Context, sound:Int) {
            //AudioManager.FLAG_SHOW_UI 调整音量时显示系统音量进度条 , 0 则不显示
            //AudioManager.FLAG_ALLOW_RINGER_MODES 是否铃声模式
            //AudioManager.FLAG_VIBRATE 是否震动模式
            //AudioManager.FLAG_SHOW_VIBRATE_HINT 震动提示
            //AudioManager.FLAG_SHOW_SILENT_HINT 静音提示
            //AudioManager.FLAG_PLAY_SOUND 调整音量时播放声音
            (context.getSystemService(Context.AUDIO_SERVICE) as AudioManager).setStreamVolume(AudioManager.STREAM_MUSIC, sound, AudioManager.FLAG_PLAY_SOUND)
        }


        /**
         * 获取通知的音量
         *
         * STREAM_VOICE_CALL 通话
         * STREAM_SYSTEM 系统
         * STREAM_RING 铃声
         * STREAM_MUSIC 媒体音量
         * STREAM_ALARM 闹钟
         * STREAM_NOTIFICATION 通知
         */
        fun getNotificationSound(context: Context) =
            (context.getSystemService(Context.AUDIO_SERVICE) as AudioManager).getStreamVolume(AudioManager.STREAM_NOTIFICATION)

        /**
         * 获取通知的最大音量
         *
         * STREAM_VOICE_CALL 通话
         * STREAM_SYSTEM 系统
         * STREAM_RING 铃声
         * STREAM_MUSIC 媒体音量
         * STREAM_ALARM 闹钟
         * STREAM_NOTIFICATION 通知
         */
        fun getMaxNotificationSound(context: Context) =
            (context.getSystemService(Context.AUDIO_SERVICE) as AudioManager).getStreamMaxVolume(AudioManager.STREAM_NOTIFICATION)

        /**
         * 设置通知的音量
         */
        fun setNotificationSound(context: Context, sound:Int) {
            //AudioManager.FLAG_SHOW_UI 调整音量时显示系统音量进度条 , 0 则不显示
            //AudioManager.FLAG_ALLOW_RINGER_MODES 是否铃声模式
            //AudioManager.FLAG_VIBRATE 是否震动模式
            //AudioManager.FLAG_SHOW_VIBRATE_HINT 震动提示
            //AudioManager.FLAG_SHOW_SILENT_HINT 静音提示
            //AudioManager.FLAG_PLAY_SOUND 调整音量时播放声音
            (context.getSystemService(Context.AUDIO_SERVICE) as AudioManager).setStreamVolume(AudioManager.STREAM_NOTIFICATION, sound, AudioManager.FLAG_PLAY_SOUND)
        }

触摸提示音

        /**
         * 设置触摸提示音
         */
        fun setTouchSound(context: Context, isEnable:Boolean){
            Settings.System.putInt(context.contentResolver, Settings.System.SOUND_EFFECTS_ENABLED, if (isEnable) 1 else 0)
        }

        /**
         * 获取触摸提示音 1=开启 0=关闭
         */
        fun getTouchSound(context: Context) =  Settings.System.getInt(context.contentResolver, Settings.System.SOUND_EFFECTS_ENABLED)

设置系统语言

import android.content.Context;
import android.content.res.Configuration;
import java.lang.reflect.Method;
import java.util.Locale;

/**
 * 更新系统语言
 */
public class UpdateLanguageUtils {

    /**
     * 获取当前系统语言
     * @return
     */
    public static Locale getCurrentLocale(Context context){
        Locale locale = context.getResources().getConfiguration().locale;
        return locale;
    }


    public static void updateLanguage(Locale locale) {
        try {
            Object objIActMag, objActMagNative;

            Class clzIActMag = Class.forName("android.app.IActivityManager");

            Class clzActMagNative = Class
                    .forName("android.app.ActivityManagerNative");

            Method mtdActMagNative$getDefault = clzActMagNative
                    .getDeclaredMethod("getDefault");

            objIActMag = mtdActMagNative$getDefault.invoke(clzActMagNative);

            Method mtdIActMag$getConfiguration = clzIActMag
                    .getDeclaredMethod("getConfiguration");

            Configuration config = (Configuration) mtdIActMag$getConfiguration
                    .invoke(objIActMag);

            config.locale = locale;

            Class clzConfig = Class
                    .forName("android.content.res.Configuration");
            java.lang.reflect.Field userSetLocale = clzConfig
                    .getField("userSetLocale");
            userSetLocale.set(config, true);

            Class[] clzParams = { Configuration.class };

            Method mtdIActMag$updateConfiguration = clzIActMag
                    .getDeclaredMethod("updateConfiguration", clzParams);

            mtdIActMag$updateConfiguration.invoke(objIActMag, config);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

SystemUI部分功能禁用设置

举例:设置是否可以下拉状态栏

        /**
         * 是否启用可以下拉状态栏
         */
        @SuppressLint("WrongConstant")
        fun setEnableStatusBarService(application: Application, isEnable: Boolean){
            val statusBarManager = application.getSystemService("statusbar")
            val method: Method = Class.forName("android.app.StatusBarManager").getMethod("disable", Int::class.java)
            if (isEnable){
                //恢复下拉状态栏
                method.invoke(statusBarManager, 0)
            } else{
                //禁止下拉状态栏
                method.invoke(statusBarManager, 65536)
            }
        }

其他可以设置的功能

    val DISABLE_BACK = 4194304 //禁用返回
    val DISABLE_CLOCK = 8388608 //禁用时钟
    val DISABLE_EXPAND = 65536 //禁用下拉状态栏
    val DISABLE_HOME = 2097152 //禁用主页
    val DISABLE_MASK = 67043328 //禁用更多?

    @Deprecated("")
    val DISABLE_NAVIGATION = 18874368 //禁用导航
    val DISABLE_NONE = 0                //禁用无
    val DISABLE_NOTIFICATION_ALERTS = 262144 //禁用通知警报
    val DISABLE_NOTIFICATION_ICONS = 131072 //禁用通知图标
    val DISABLE_NOTIFICATION_TICKER = 524288 //禁用通知代码
    val DISABLE_RECENT = 16777216   //禁用最近
    val DISABLE_SEARCH = 33554432   //禁用搜索
    val DISABLE_SYSTEM_INFO = 1048576   //禁用系统信息

同时禁用状态栏与导航栏

        /**
         * 禁用状态栏与导航栏
         *
         * https://stackoverflow.com/questions/29969086/how-to-disable-status-bar-click-and-pull-down-in-android#:~:text=Disable%20Android%20StatusBar%20expand/pull%2Ddown
         *
         * @param context 上下文
         * @param disable 是否禁用
         */
        fun disableStatusBar(context: Application, disable: Boolean) {
            // Read from property or pass it in function, whatever works for you!
//        val disable: Boolean =
//            SystemProperties.getBoolean(context, "supercool.status.bar.disable", true)

            @SuppressLint("WrongConstant")
            val statusBarService = context.getSystemService(
                "statusbar"  // Context.STATUS_BAR_SERVICE
            )
            val statusBarManager: Class<*>?
            try {
                statusBarManager = Class.forName("android.app.StatusBarManager")
                try {
                    val disableStatusBarFeatures: Method =
                        statusBarManager.getMethod("disable", Int::class.javaPrimitiveType)
                    try {
                        disableStatusBarFeatures.isAccessible = true
                        if (disable) {
                            disableStatusBarFeatures.invoke(statusBarService,
                                0x00010000 // View.STATUS_BAR_DISABLE_EXPAND
                                        or 0x00020000 // View.STATUS_BAR_DISABLE_NOTIFICATION_ICONS
                                        or 0x00040000 // View.STATUS_BAR_DISABLE_NOTIFICATION_ALERTS
                                        or 0x00080000 // View.STATUS_BAR_DISABLE_NOTIFICATION_TICKER
                                        or 0x00100000 // View.STATUS_BAR_DISABLE_SYSTEM_INFO
                                        or 0x00200000 // View.STATUS_BAR_DISABLE_HOME
                                        or 0x00400000 // View.STATUS_BAR_DISABLE_BACK
                                        or 0x00800000 // View.STATUS_BAR_DISABLE_CLOCK
                                        or 0x01000000 // View.STATUS_BAR_DISABLE_RECENT
                                        or 0x02000000 // View.STATUS_BAR_DISABLE_SEARCH
                                        or 0x04000000 // View.STATUS_BAR_DISABLE_ONGOING_CALL_CHIP
                            )
                        } else {
                            disableStatusBarFeatures.invoke(statusBarService, 0x00000000)
                        }
                    } catch (e: java.lang.Exception) {
                    }
                } catch (e: java.lang.Exception) {
                }
            } catch (e: java.lang.Exception) {
            }
        }

framework源码:

package android.app;

import android.content.Context;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.util.Slog;
import com.android.internal.statusbar.IStatusBarService;
import com.android.internal.statusbar.IStatusBarService.Stub;

public class StatusBarManager {
    public static final int DISABLE_BACK = 4194304;
    public static final int DISABLE_CLOCK = 8388608;
    public static final int DISABLE_EXPAND = 65536;
    public static final int DISABLE_HOME = 2097152;
    public static final int DISABLE_MASK = 67043328;
    @Deprecated
    public static final int DISABLE_NAVIGATION = 18874368;
    public static final int DISABLE_NONE = 0;
    public static final int DISABLE_NOTIFICATION_ALERTS = 262144;
    public static final int DISABLE_NOTIFICATION_ICONS = 131072;
    public static final int DISABLE_NOTIFICATION_TICKER = 524288;
    public static final int DISABLE_RECENT = 16777216;
    public static final int DISABLE_SEARCH = 33554432;
    public static final int DISABLE_SYSTEM_INFO = 1048576;
    public static final int NAVIGATION_HINT_BACK_ALT = 1;
    public static final int WINDOW_NAVIGATION_BAR = 2;
    public static final int WINDOW_STATE_HIDDEN = 2;
    public static final int WINDOW_STATE_HIDING = 1;
    public static final int WINDOW_STATE_SHOWING = 0;
    public static final int WINDOW_STATUS_BAR = 1;
    private Context mContext;
    private IStatusBarService mService;
    private IBinder mToken = new Binder();

    StatusBarManager(Context context) {
        this.mContext = context;
    }

    private synchronized IStatusBarService getService() {
        if (this.mService == null) {
            this.mService = Stub.asInterface(ServiceManager.getService(Context.STATUS_BAR_SERVICE));
            if (this.mService == null) {
                Slog.w("StatusBarManager", "warning: no STATUS_BAR_SERVICE");
            }
        }
        return this.mService;
    }

    public void disable(int what) {
        try {
            IStatusBarService svc = getService();
            if (svc != null) {
                svc.disable(what, this.mToken, this.mContext.getPackageName());
            }
        } catch (RemoteException ex) {
            throw new RuntimeException(ex);
        }
    }

    public void expandNotificationsPanel() {
        try {
            IStatusBarService svc = getService();
            if (svc != null) {
                svc.expandNotificationsPanel();
            }
        } catch (RemoteException ex) {
            throw new RuntimeException(ex);
        }
    }

    public void collapsePanels() {
        try {
            IStatusBarService svc = getService();
            if (svc != null) {
                svc.collapsePanels();
            }
        } catch (RemoteException ex) {
            throw new RuntimeException(ex);
        }
    }

    public void expandSettingsPanel() {
        try {
            IStatusBarService svc = getService();
            if (svc != null) {
                svc.expandSettingsPanel();
            }
        } catch (RemoteException ex) {
            throw new RuntimeException(ex);
        }
    }

    public void setIcon(String slot, int iconId, int iconLevel, String contentDescription) {
        try {
            IStatusBarService svc = getService();
            if (svc != null) {
                svc.setIcon(slot, this.mContext.getPackageName(), iconId, iconLevel, contentDescription);
            }
        } catch (RemoteException ex) {
            throw new RuntimeException(ex);
        }
    }

    public void removeIcon(String slot) {
        try {
            IStatusBarService svc = getService();
            if (svc != null) {
                svc.removeIcon(slot);
            }
        } catch (RemoteException ex) {
            throw new RuntimeException(ex);
        }
    }

    public void setIconVisibility(String slot, boolean visible) {
        try {
            IStatusBarService svc = getService();
            if (svc != null) {
                svc.setIconVisibility(slot, visible);
            }
        } catch (RemoteException ex) {
            throw new RuntimeException(ex);
        }
    }

    public static String windowStateToString(int state) {
        if (state == 1) {
            return "WINDOW_STATE_HIDING";
        }
        if (state == 2) {
            return "WINDOW_STATE_HIDDEN";
        }
        if (state == 0) {
            return "WINDOW_STATE_SHOWING";
        }
        return "WINDOW_STATE_UNKNOWN";
    }
}

开启关闭有线网络


import android.os.IBinder;
import android.util.Log;
import java.lang.reflect.Method;

public class Eth0Util {

    // INetworkManagementService mNwService;
    Object mINetworkManagementServiceProxy;

    public void init() {
        // IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE);
        // mNwService = INetworkManagementService.Stub.asInterface(b);
        try {
            Class<?> smClass = Class.forName("android.os.ServiceManager");
            Method getService = smClass.getMethod("getService", new Class[]{String.class});
            getService.setAccessible(true);
            IBinder networkManagementBinder = (IBinder) getService.invoke(null, new Object[]{"network_management"});

            Class<?> iNMSClass = Class.forName("android.os.INetworkManagementService$Stub");
            Method asInterface = iNMSClass.getMethod("asInterface", new Class[]{IBinder.class});
            asInterface.setAccessible(true);
            mINetworkManagementServiceProxy = asInterface.invoke(null, new Object[]{networkManagementBinder});
            Log.e("zh", "init: mINetworkManagementServiceProxy = " + mINetworkManagementServiceProxy);
        } catch (Throwable e) {
            Log.e("zh", "init: get INetworkManagementServiceProxy failed !!! ");
        }
    }

    public void openEth0() {
        try {
            Method setInterfaceUp = mINetworkManagementServiceProxy.getClass().getMethod("setInterfaceUp", new Class[]{String.class});
            setInterfaceUp.setAccessible(true);
            setInterfaceUp.invoke(mINetworkManagementServiceProxy, "eth0");
        } catch (Throwable e) {
            Log.e("zh", "openEth0: failed !!! ");
        }
    }

    public void closeEth0() {
        try {
            Method setInterfaceDown = mINetworkManagementServiceProxy.getClass().getMethod("setInterfaceDown", new Class[]{String.class});
            setInterfaceDown.setAccessible(true);
            setInterfaceDown.invoke(mINetworkManagementServiceProxy, "eth0");
            Log.e("zh", "111111111111111111");
        } catch (Throwable e) {
            Log.e("zh", "closeEth0: failed !!! ");
        }
    }
}

 

End

posted on 2022-04-02 17:40  观心静  阅读(653)  评论(0编辑  收藏  举报