5.在关机界面添加重启功能
Google原生的Android系统一般是没有“重启”这个选项的。有时候重启也是不可或缺的一个Feature,那么如何在源码环境下添加这个选项呢?
1. 在frameworks\base\core\res\res\values\strings.xml
中添加标签:
Reboot
当然这只是英语语系的,需要添加其它语系的标示,把"Reboot" 替换成其它语言。
2. 在alps\frameworks\base\core\res\res\drawable-hdpi 中添加图标:
zms_ic_lock_power_reboot.png
3. 打开frameworks\policies\base\phone\com\android\internal\policy\impl\GlobalActions.java
大概在这个文件的261行有这样的代码:
mItems = Lists.newArrayList(
// silent mode
mSilentModeToggle,
// next: airplane mode
mAirplaneModeOn,
// last: power off
在这里,我们添加power reboot 的新的item.
具体这个mItems 更新为如下:
mItems = Lists.newArrayList(
// silent mode
mSilentModeToggle,
// next: airplane mode
mAirplaneModeOn,
// last: power off
new SinglePressAction(
com.android.internal.R.drawable.ic_lock_power_off,
R.string.global_action_power_off) {
public void onPress() {
// shutdown by making sure radio and power are handled
accordingly.
ShutdownThread.shutdown(mContext, true);
}
public boolean showDuringKeyguard() {
return true;
}
public boolean showBeforeProvisioning() {
return true;
}
}//zms add start
,
new SinglePressAction(
com.android.internal.R.drawable.zms_ic_lock_power_reboot,
R.string.zms_global_action_power_reboot) {
public void onPress() {
// reboot by making sure radio and power are handled
accordingly.
ShutdownThread.reboot(mContext, null, true);
}
public boolean showDuringKeyguard() {
return true;
}
public boolean showBeforeProvisioning() {
return true;
}
}
//zms add end.
);
经过这样的添加/修改后,这项feature 即可运行。
注意如果测试的话,因为有修改framework 中的文件,最好new 一下整个工程。
另外还需要修改一下ShutdownThread.java 中的那个dialog 显示描述,不然将依旧看到“关机”的信息。
位置:frameworks/base/services/java/com/android/server/power/ShutdownThread.java
不同版本的代码位置可能有所差别,可以在根目录下find一下:
find -name ShutdownThread.java
如下:
sConfirmDialog = new AlertDialog.Builder(context)
.setTitle((mReboot && !mRebootSafeMode)
-
? com.android.internal.R.string.reboot_title
- (mRebootSafeMode ? com.android.internal.R.string.reboot_safemode_title
- com.android.internal.R.string.power_off))
.setMessage(resourceId)
.setPositiveButton(com.android.internal.R.string.yes, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
beginShutdownSequence(context);
if (sConfirmDialog != null) {
sConfirmDialog = null;
}
}
})
.setNegativeButton(com.android.internal.R.string.no, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
synchronized (sIsStartedGuard) {
sIsStarted = false;
}
if (sConfirmDialog != null) {
sConfirmDialog = null;
}
}
})
.create();
6.使用init.rc触发脚本实现隐藏内置应用
【实现逻辑】
通过在property_service.c中设置标志位,在设置中实现接口改变标志位,
使用init.rc中声明的服务来侦听标志位的变化,显式启动声明的服务,执行对应的脚本,把应用后缀从apk重命名为bak,从而实现隐藏(显示逻辑相反)。
【实现步骤】以隐藏Google Play Store(system/priv-app/Phonesky.apk)为例:
1.首先在system/core/init/property_service.c中声明并初始化标志位,0为隐藏,1为显示,默认隐藏
{ “app.launcher.start”, AID_SYSTEM, 0},
- { “app.phonesky.show”, AID_SYSTEM, 0}, //Add By zj
{ “cdma.”, AID_RADIO, 0 }, //Add by gfzhu VIA
2.在设置的开发者选项中实现对应的接口:
文件路径:packages/apps/Settings/src/com/android/settings/DevelopmentSettings.java
①声明和初始化:
private static final String SHOW_PHONESKY = “show_phonesky”;
private CheckBoxPreference mShowPhonesky;
mShowPhonesky = findAndInitCheckboxPref(SHOW_PHONESKY);
②CheckBox的逻辑:
(BatteryManager.BATTERY_PLUGGED_AC | BatteryManager.BATTERY_PLUGGED_USB) : 0);
-
} else if (preference == mShowPhonesky) { // ZJ Add
-
if(mShowPhonesky.isChecked())
-
{
-
SystemProperties.set("app.phonesky.show","1");
-
}else{
-
SystemProperties.set("app.phonesky.show","0");
-
}
} else if (preference == mBtHciSnoopLog) {
③增加一个Preference:
packages/apps/Settings/res/xml/development_prefs.xml
android:targetClass=“com.android.settings.SetFullBackupPassword” />
-
<CheckBoxPreference
-
android:key="show_phonesky"
-
android:title="@string/show_phonesky"
-
/>
<CheckBoxPreference
④添加对应语言的string字符:
Show Google Play Store
⑤设置中新增一个监听,初始化Checkbox的逻辑:
packages/apps/Settings/src/com/android/settings/BootReceiver.java
内容如下:
package com.android.settings;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.SystemClock;
import android.util.Log;
import android.os.SystemProperties;
public class BootReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context arg0, Intent arg1) {
// TODO Auto-generated method stub
String action = arg1.getAction();
if(action.equals(Intent.ACTION_BOOT_COMPLETED))
{
SharedPreferences shared = arg0.getSharedPreferences(“com.android.settings_preferences”, Context.MODE_PRIVATE);
boolean show_phonesky = shared.getBoolean(“show_phonesky”, false);
if(show_phonesky){
SystemProperties.set(“app.phonesky.show”,“1”);
}else{
SystemProperties.set(“app.phonesky.show”,“0”);
}
}
}
}
⑥在Settings的AndroidManifest文件中添加BroadcastReceiver的权限和声明:
3.在init.rc中添加对应的服务和触发条件:
路径:mediatek/config/esky27_tb_ccn_mlc_kk/init.rc
+# ZJ Add START
+#Hide or Show Google Play Dynamicly
+#disabled:服务不会自动运行,必须显式地通过服务器来启动。
+#oneshot:当此服务退出时不会自动重启。
+service hidePhonesky /system/bin/hidePhonesky
-
disabled
-
oneshot
+service showPhonesky /system/bin/showPhonesky
-
disabled
-
oneshot
+#on property:sys.boot_completed=1
+# start renamePhonesky
+on property:app.phonesky.show=1
- start showPhonesky
+on property:app.phonesky.show=0
- start hidePhonesky
+# ZJ Add END
4.隐藏和显示应用的脚本:
隐藏应用:vendor/ThirdParty/App/dte/hidePhonesky
内容:
#!/system/bin/sh
#!/system/bin/busybox
mount -o remount,rw /system;
mv /system/priv-app/Phonesky.apk /system/priv-app/Phonesky.bak
显示应用:vendor/ThirdParty/App/dte/showPhonesky
内容:
#!/system/bin/sh
#!/system/bin/busybox
mount -o remount,rw /system;
mv /system/priv-app/Phonesky.bak /system/priv-app/Phonesky.apk
5.拷贝脚本到system/bin目录下:
参考以下格式添加到对应的mk文件:
+#添加重命名GooglePlay脚本
+PRODUCT_COPY_FILES += \
-
vendor/ThirdParty/App/dte/hidePhonesky:system/bin/hidePhonesky \
-
vendor/ThirdParty/App/dte/showPhonesky:system/bin/showPhonesky \
-
vendor/ThirdParty/App/dte/Phonesky.bak:system/priv-app/Phonesky.bak
7.修改链接电脑时的“总线已报告设备描述”显示名称
在:Android USB Gadget Driver中进行修改
static const char longname[] = “Gadget Android”;
/* Default vendor and product IDs, overridden by userspace */
#define VENDOR_ID 0x0BB4
#define PRODUCT_ID 0x0001
/* Default manufacturer and product string , overridden by userspace */
// 制造商
#define MANUFACTURER_STRING “MediaTek”
// 设备描述,可以在“总线已报告设备描述”中看到
#define PRODUCT_STRING “MTP”
#define USB_LOG “USB”
8.开机动画包bootanimation的制作规范
除了一些特别厂商,其他大部分Android设备的开机动画包的文件名都是bootanimation.zip。可以通过adb查看system/media/路径查看,如果没有一般会调用系统开机动画,即android字样。这点三星有些不同,它的格式是bootsamsung.qmg。今天只说一下具有普适性的bootanimation.zip的制作。
这是三星的:
一、保证bootanimation.zip压缩包下的图片Size和格式完全统一
二、请写规范的配置文件desc.txt
desc.txt每个参数的实际意义,以如下的case为例:
480 854 10
p 1 0 part0
p 0 0 part1
1.第一行的参数前两位480和854分别表示要显示动画的width和height. 默认情况下应该与Display的width和height一致,如果设置比Display的size要小,则动画会居中显示,周边将用黑框填充.
2.第一行的第三个参数10是定义动画播放的预订帧率(FPS),这个帧率fps是指:每秒动画播放的帧数。此帧数是一个理想值,并不一定代表动画实际帧率,假设预订帧率为FPS_I,预订每一帧解析的时间t_I, 则t_I=1/FPS_I。
实际帧率的规则是:假设某一帧从解析到渲染耗时为t_r,当t_r<=t_l,则渲染完这一帧后,动画这个thread会sleep(t_l-t_r)的时间,也就说这一帧最后的耗时就t_l;假设某一帧从解析到渲染耗时为t_r,当t_r>t_l,则渲染完这一帧后,动画这个thread会马上开始下一帧,也就说这一帧最后的耗时就t_r。所以,desc.txt内设置的这个帧率并不能代表动画的实际帧率,实际的帧率是和系统开机的performance有关,因此不是说在desc.txt设置帧率越大越好,反而容易出现当某一帧耗时较长,就容易给用户某一帧卡顿的体验,目前这个FPS的值一般设置在13左右。当然,设置FPS为13并不是说系统的performance比较低,本身在开机动画阶段,系统进入Bootup Android阶段,许多进程需要启动,系统的主要工作应该集中与开机启动的进程,因此不建议动画的图片过于复杂,导致系统开机的Performance变差。
3.第二行和第三行情况类似,一般用于分别设置顺序播放和无限循环播放的相关参数.第一个参数p是google default的设计,请保留以p开头。第二个参数1表示这一行对应folder所需要循环播放的次数,如果是0则表示是无限循环播放,直到系统ready后通过被动退出。第三个参数0表示这一行对应folder里面的每一帧图片依次解析渲染完成后,要进入下一个循环,动画这个线程需要pause多久。第四个参数part0表示对应设置规则的folder的path。
Note1:默认的设计,都是将顺序播放的动画放在一个folder,定义这个folder所需要循环的次数;在无限循环的folder内放置一张图片,保证动画没有收到退出指令的时候,动画可以一直显示.
Note2:由于循环播放的folder中的每帧都是以纹理对象存储在纹理内存中再upload到GPU做渲染的,以便下次循环播放不需要重新解析.如果动画包中的图片太多或者图片的size很大时,则会导致占用较多的memory,因此为保证开机的performance,开机动画不建议太复杂.
注意事项:
1.压缩包里面除了desc.txt以外不能存在其他非图片格式的文件,否则会引起bootanimation程序崩溃,所以在windows系统下打包bootanimation.zip的时候,如果浏览过图片,要删掉生成隐藏文件Thumbs.db,或者在linux下打包。
2.压缩包内的文件结构是单层的,就是双击压缩包预览,直接看到part0,part1文件夹和sesc.txt文件,而不能是bootanimation文件夹。
3.desc.txt文件内容不要有多余的空行
4.制作完成后可以adb push到设备的/system/media/下面重启看一下效果。
9.修改内核版本编译信息中的user和host字段
有客户需要修改内核版本号中的字段,如下图红线标注区域:
修改方法:
以修改为“qizi@qizi001"为例:
打开kernel/scripts/mkcompile_h,做如下修改即可:
----------------------------------------------------------------------------------
@@ -73,8 +73,8 @@ UTS_TRUNCATE=“cut -b -$UTS_LEN”
echo \#define UTS_VERSION \“`echo $UTS_VERSION | $UTS_TRUNCATE`\”
- echo \#define LINUX_COMPILE_BY \“`echo $LINUX_COMPILE_BY | $UTS_TRUNCATE`\”
- echo \#define LINUX_COMPILE_HOST \“`echo $LINUX_COMPILE_HOST | $UTS_TRUNCATE`\”
+ echo \#define LINUX_COMPILE_BY \“`echo “qizi” | $UTS_TRUNCATE`\”
+ echo \#define LINUX_COMPILE_HOST \“`echo “qizi001” | $UTS_TRUNCATE`\”
echo \#define LINUX_COMPILER \“`$CC -v 2>&1 | tail -n 1`\”
) > .tmpcompile
----------------------------------------------------------------------------------
附:
在对应的buildinfo文件中修改ro.build.user和ro.build.host两个属性不能达到预期效果。
10.Android 4.4限制Root权限的逻辑
android 4.4 版本后,su 权限严重被限制, 如无法直接访问data 区域,无法直接remount system image, 无法设置system property。
Google 不遗余力的提高android系统的安全性, 而针对su 这个即令人恨,又令人爱的命令,就痛下杀手。主要体现在三个方面:
1. 限制user 版本adbd process 的capabilities bound set。循环CAPBSET_DROP 动作,将Process的root capabilities 进行了强行限制。仅仅保留了CAP_SETUID, CAP_SETGID 这两项,用于run-as使用,可参考源码中system/core/adb/adb.c 中的drop_capabilities_bounding_set_if_need 函数。这样导致的情况是,在user 版本中usb debug 的su 受到极大的限制,仅仅能够模拟对应的uid/gid,而无法拿去真正的root 权限。
2. 限制所有app 的capabilities bound set, 在android 4.4 上,zygote fork app 时,特意对所有fork 出来的子进程,进行了CAPBSET_DROP 动作,将Process 的root capabilities 进行了强行限制。 使得即使这些APK 徒有Root 权限,而无真实的capabilites.
这样导致的情况是, app 执行su 时,其权限受到了严格的管控,比如无法逃脱DAC 权限管控。但因为依旧具有root uid/gid, 所以在framework 层的permission 限制上依旧畅通无阻。
3. SElinux 权限限制。 在user 版本上,没有导入有效的SElinux policy, 这样一旦本身受SElinux 限制的process 使用su 时,同样会受到SElinux 的限制。 目前只有4个process 会受到此影响,即zygote, netd, installd, vold.消除这种限制的手法即是external/sepolicy/android.mk 里面的
ifeq ($(TARGET_BUILD_VARIANT),user)
BOARD_SEPOLICY_IGNORE+=external/sepolicy/su.te
else
BOARD_SEPOLICY_IGNORE+=external/sepolicy/su_user.te
endif
更新成:
ifeq ($(TARGET_BUILD_VARIANT),user)
BOARD_SEPOLICY_IGNORE+=external/sepolicy/su_user.te
else
BOARD_SEPOLICY_IGNORE+=external/sepolicy/su_user.te
endif
11.Android自动连接WiFi优先级规则,以及查看已连接WiFi的密码
目前Android的WiFi自动连接的优先级规则如下:
1、priority值的范围设定为[0,1000000),如果超出此范围则会reset;
2、最近连接过的AP拥有最高priority,在自动连接中会首先尝试连接它;
3、未连接过但是扫描到的AP,按其信号值强弱排序,越强的显示靠前,但是,还得综合
AP的安全因素,基本情况是:WPA/WPA2 > WEP > signal level high > signal level low > noise low > noise
high
4、如果是预置的AP,可能会人为设定其最高的priority;
看一下源码,代码路径:frameworks/base/wifi/java/android/net/wifi/
WifiConfigStore.java
boolean selectNetwork(int netId) {
if (VDBG) localLog(“selectNetwork”, netId);
if (netId == INVALID_NETWORK_ID) return false;
// Reset the priority of each network at start or if it goes too high.
if (mLastPriority == -1 || mLastPriority > 1000000) {
Xlog.d(TAG, “Need to reset the priority, mLastPriority:” + mLastPriority);
for(WifiConfiguration config : mConfiguredNetworks.values()) {
if (config.networkId != INVALID_NETWORK_ID) {
config.priority = 0;
addOrUpdateNetworkNative(config);
}
}
mLastPriority = 0;
}
// Set to the highest priority and save the configuration.
WifiConfiguration config = new WifiConfiguration();
config.networkId = netId;
config.priority = ++mLastPriority;
addOrUpdateNetworkNative(config);
mWifiNative.saveConfig();
/* Enable the given network while disabling all other networks */
enableNetworkWithoutBroadcast(netId, true);
/* Avoid saving the config & sending a broadcast to prevent settings
- from displaying a disabled list of networks */
return true;
}
有时候,我们会忘记已连接WiFi的密码,应用市场也有相关的应用可以帮我们读取。其实如有有Root权限,用RE文件管理器(Root Explorer)就可以查看了。文件路径:
/data/misc/wifi/sockets/wpa_supplicant.conf
每一个network包裹起来的就是一个连接过的WiFi热点,其中ssid是名字,psk就是密码了,也可以看到其他信息,包括加密类型key_mgmt和优先级priority,是否自动连接autojoin等,如下图:
12.让一个应用不在“全部应用列表”中显示
首先修改一下这个文件:
packages/apps/Settings/src/com/android/settings/applications/ApplicationsState.java
下面是Git Diff 的结果:
diff --git a/packages/apps/Settings/src/com/android/settings/applications/ApplicationsState.java b/packages/apps/Settings/src/com/android/settings/applications/ApplicationsState.java
index e87d7cf…3f1a507 100644 (file)
— a/packages/apps/Settings/src/com/android/settings/applications/ApplicationsState.java
+++ b/packages/apps/Settings/src/com/android/settings/applications/ApplicationsState.java
@@ -33,6 +33,8 @@ import java.util.HashMap;
import java.util.List;
import java.util.regex.Pattern;
+import android.os.TCToolManager;
/**
-
Keeps track of information about all installed applications, lazy-loading
-
as needed.
@@ -42,6 +44,8 @@ public class ApplicationsState {
static final boolean DEBUG = false;
static final boolean DEBUG_LOCKING = false;
-
final TCToolManager mTCTool;
public static interface Callbacks {
public void onRunningStateChanged(boolean running);
public void onPackageListChanged();
@@ -404,6 +408,8 @@ public class ApplicationsState {
mThread.start();
mBackgroundHandler = new BackgroundHandler(mThread.getLooper());
-
mTCTool = (TCToolManager)mContext.getSystemService(Context.TCHIP_TOOL_SERVICE);
// Only the owner can see all apps.
if (UserHandle.myUserId() == 0) {
mRetrieveFlags = PackageManager.GET_UNINSTALLED_PACKAGES |
@@ -548,6 +554,13 @@ public class ApplicationsState {
if (DEBUG_LOCKING) Log.v(TAG, “rebuild acquired lock”);
AppEntry entry = getEntryLocked(info);
entry.ensureLabel(mContext);
-
if (mTCTool.isHide(info.packageName)) {
-
if(TCToolManager.DEBUG)
-
TCToolManager.Log("hide app:" + info.loadLabel(mPm) + ":" + info.packageName);
-
continue;
-
}
if (DEBUG) Log.i(TAG, "Using " + info.packageName + ": " + entry);
filteredApps.add(entry);
if (DEBUG_LOCKING) Log.v(TAG, “rebuild releasing lock”);
然后添加以下三个文件到指定路径:
frameworks/base/core/java/android/os/ITCToolService.aidl:
/**
-
Copyright © 2007, The Android Open Source Project
-
Licensed under the Apache License, Version 2.0 (the “License”);
-
you may not use this file except in compliance with the License.
-
You may obtain a copy of the License at
-
http://www.apache.org/licenses/LICENSE-2.0
-
Unless required by applicable law or agreed to in writing, software
-
distributed under the License is distributed on an “AS IS” BASIS,
-
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-
See the License for the specific language governing permissions and
-
limitations under the License.
*/
package android.os;
import android.content.Context;
/** {@hide} */
interface ITCToolService
{
String getVersion();
boolean isHide(String name);
}
frameworks/base/core/java/android/os/TCToolManager.java:
1 /*
2 * Copyright © 2006 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the “License”);
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an “AS IS” BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 package android.os;
18
19 import android.util.Log;
20
21
22
23 public class TCToolManager
24 {
25 private static final String TAG = “TCToolManager”;
26 public static final boolean DEBUG = true;
27
28 ITCToolService mService;
29
30 /** @hide */
31 public TCToolManager(ITCToolService service)
32 {
33 mService = service;
34 Log.d(TAG, "version: " + getVersion());
35 }
36
37 public String getVersion() {
38
39 try {
40 return mService.getVersion();
41 } catch (RemoteException e) {
42 }
43
44 return null;
45 }
46
47 public boolean isHide(String name) {
48 try {
49 return mService.isHide(name);
50 } catch (RemoteException e) {
51 }
52
53 return false;
54 }
55
56 // debug log out
57 static public void Log(String log) {
58 // TODO Auto-generated method stub
59 Log.d(TAG, log);
60 }
61 }
frameworks/base/services/java/com/android/server/TCToolService.java:
1 /*
2 * Copyright © 2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the “License”);
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an “AS IS” BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 package com.android.server;
18
19 import android.content.Context;
20 import android.content.Intent;
21 import android.content.SharedPreferences;
22 import android.os.ITCToolService;
23 import android.os.storage.StorageEventListener;
24 import android.os.storage.StorageManager;
25 import android.preference.PreferenceManager;
26 import android.util.Log;
27 import android.util.Slog;
28
29 import java.io.BufferedReader;
30 import java.io.DataOutputStream;
31 import java.io.File;
32 import java.io.FileInputStream;
33 import java.io.FileNotFoundException;
34 import java.io.FileOutputStream;
35 import java.io.IOException;
36 import java.io.InputStreamReader;
37 import java.util.ArrayList;
38 import java.util.Calendar;
39
40 public class TCToolService extends ITCToolService.Stub {
41 private static final String TAG = “TCToolService”;
42 private static final String VERSION = “T-CHIP tool V1.0.1”;
43 private Context mContext;
44 private final File mToolDir;
45 private boolean inited = false;
46
47 private static final boolean DEBUG = true;
48
49 private static final String FILE = “hideapk.txt”;
50
51 private static final String EXTSD_PATH = “/mnt/external_sd”;
52
53 private static final String SYS_PATH = “/system/usr/data”;
54
55 private ArrayList mHideList = new ArrayList();
56
57 private static final Object sLock = new Object();
58 private StorageManager mStorageManager = null;
59
60 public String getVersion() {
61 return VERSION;
62 }
63
64 TCToolService(Context context, File path) {
65 mContext = context;
66 mToolDir = path;
67
68 initHideLocked(false);
69 }
70
71 private void initHideLocked(boolean forceUpdate) {
72 synchronized (sLock) {
73 if (!inited || forceUpdate) {
74 inited = true;
75 initHide(forceUpdate);
76 }
77 }
78 }
79
80 private void initHide(boolean forceUpdate) {
81 File hideFile = null;
82 File outFile = new File(mToolDir, FILE);
83 boolean bWriteCfg = true;
84
85 if (!mToolDir.exists()) {
86 mToolDir.mkdirs();
87 }
88
89 if (outFile.exists() && !forceUpdate) {
90 bWriteCfg = false;
91 } else if ((hideFile = new File(EXTSD_PATH + “/” + FILE)).exists()) {
92 setSDHideState(true);
93 setSDHideDate(hideFile.lastModified());
94 if (DEBUG)
95 Log.d(TAG, “Set bSDHideExists”);
96 }
97
98 if (DEBUG && null != hideFile)
99 Log.d(TAG, "Load hideapk.txt from " + hideFile.getPath());
100
101 mHideList.clear();
102 readHideCfg(new File(SYS_PATH + “/” + FILE), mHideList);
103 readHideCfg(hideFile, mHideList);
104
105 if (bWriteCfg && !mHideList.isEmpty()) {
106 if (DEBUG)
107 Log.d(TAG, “Save hideapk.txt to app file”);
108 writeHideCfg(outFile, mHideList);
109 }
110
111 if (null == mStorageManager) {
112 mStorageManager = (StorageManager) mContext.getSystemService(Context.STORAGE_SERVICE);
113 mStorageManager.registerListener(mStorageListener);
114 }
115 }
116
117 private void writeHideCfg(File hideFile, ArrayList hidelist) {
118 DataOutputStream out = null;
119
120 try {
121 out = new DataOutputStream(new FileOutputStream(hideFile));
122 for (String hide : hidelist) {
123 // out.writeUTF(hide + “\n”);
124 out.writeBytes(hide + “\n”);
125 }
126 out.flush();
127 } catch (FileNotFoundException e) {
128 // Ignore
129 e.printStackTrace();
130 } catch (IOException e) {
131 e.printStackTrace();
132 // noinspection ResultOfMethodCallIgnored
133 hideFile.delete();
134 } finally {
135 if (out != null) {
136 try {
137 out.close();
138 } catch (IOException e) {
139 // Ignore
140 }
141 }
142 }
143 }
144
145 private void readHideCfg(File hideFile, ArrayList hidelist) {
146 if (hideFile == null || !hideFile.exists())
147 return;
148
149 try {
150 FileInputStream inStream = new FileInputStream(hideFile);
151
152 if (inStream != null) {
153 BufferedReader rbf = new BufferedReader(new InputStreamReader(inStream));
154
155 String linebuf = rbf.readLine();
156 while (linebuf != null) {
157 linebuf = linebuf.trim();
158 if (!linebuf.startsWith(“#”, 0) && linebuf.length() != 0) {
159 if (DEBUG)
160 Log.d(TAG, "readHideCfg: " + linebuf);
161 hidelist.add(linebuf);
162 }
163 linebuf = rbf.readLine();
164 }
165 return;
最后
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。
因此我收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点!不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门
如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
40 }
141 }
142 }
143 }
144
145 private void readHideCfg(File hideFile, ArrayList hidelist) {
146 if (hideFile == null || !hideFile.exists())
147 return;
148
149 try {
150 FileInputStream inStream = new FileInputStream(hideFile);
151
152 if (inStream != null) {
153 BufferedReader rbf = new BufferedReader(new InputStreamReader(inStream));
154
155 String linebuf = rbf.readLine();
156 while (linebuf != null) {
157 linebuf = linebuf.trim();
158 if (!linebuf.startsWith(“#”, 0) && linebuf.length() != 0) {
159 if (DEBUG)
160 Log.d(TAG, "readHideCfg: " + linebuf);
161 hidelist.add(linebuf);
162 }
163 linebuf = rbf.readLine();
164 }
165 return;
最后
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。
因此我收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
[外链图片转存中…(img-XclLJVK7-1715348028391)]
[外链图片转存中…(img-tvThlwQH-1715348028392)]
[外链图片转存中…(img-2NfTezYu-1715348028393)]
[外链图片转存中…(img-C8mAtzXe-1715348028394)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点!不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门
如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!