http://blog.csdn.net/zhoumushui/article/details/41623903

1.Android字库文件

打开Android源码路径:

framework/base/data/fonts/

可以看到很多ttf文件,如下图:

这些文件都是做什么用的呢?查了些资料整理下:

字库文件 用途
=======================================================

AndroidClock.ttf  用于显示不同样式时间
AndroidClock_Highlight.ttf
AndroidClock_Solid.ttf

-------------------------------------------------------------------------------------------------
Clockopia.ttf 用于锁屏界面

-------------------------------------------------------------------------------------------------
AndroidEmoji.ttf 符号字形

-------------------------------------------------------------------------------------------------
DroidNaskh-Regular.ttf 波斯,阿拉伯语,乌尔都语字库
DroidNaskhUI-Regular.ttf

-------------------------------------------------------------------------------------------------
DroidSansArmenian.ttf 亚美尼亚语

-------------------------------------------------------------------------------------------------
DroidSansEthiopic-Regular.ttf 阿姆哈拉语、提格雷语(埃塞俄比亚)

-------------------------------------------------------------------------------------------------
DroidSansFallback.ttf 中文字库,CJK中日韩统一字符

-------------------------------------------------------------------------------------------------
DroidSansGeorgian.ttf 格鲁吉亚语

-------------------------------------------------------------------------------------------------
DroidSansHebrew-Bold.ttf 希伯来语
DroidSansHebrew-Regular.ttf

-------------------------------------------------------------------------------------------------
DroidSansMono.ttf 西里尔和拉丁字母扩充附加

-------------------------------------------------------------------------------------------------
DroidSerif-Bold.ttf 拉丁字母:衬线体
DroidSerif-BoldItalic.ttf
DroidSerif-Italic.ttf
DroidSerif-Regular.ttf

-------------------------------------------------------------------------------------------------
MTLmr3m.ttf 繁体中文字库

-------------------------------------------------------------------------------------------------
NanumGothic.ttf 谚文字母(朝鲜语、韩语)

-------------------------------------------------------------------------------------------------
padauk.ttf 官方缅甸语字库
ZawgyiOne.ttf 民间缅甸语字库

-------------------------------------------------------------------------------------------------
Roboto-Bold.ttf 欧洲使用的拉丁、西里尔字母
Roboto-Regular.ttf
External/noto-fonts

-------------------------------------------------------------------------------------------------
NotoColorEmoji.ttf 表情字符

-------------------------------------------------------------------------------------------------
NotoSansBengaliUI-Bold.ttf 孟加拉语字库
NotoSansBengaliUI-Regular.ttf

-------------------------------------------------------------------------------------------------
NotoSansDevanagariUI-Bold.ttf 印度语字库
NotoSansDevanagariUI-Regular.ttf

-------------------------------------------------------------------------------------------------
NotoSansKannadaUI-Bold.ttf 卡纳达语字库(印度)
NotoSansKannadaUI-Regular.ttf

-------------------------------------------------------------------------------------------------
NotoSansKhmerUI-Bold.ttf 高棉语字库(柬埔寨)
NotoSansKhmerUI-Regular.ttf

-------------------------------------------------------------------------------------------------
NotoSansLaoUI-Bold.ttf 老挝语字库
NotoSansLaoUI-Regular.ttf

-------------------------------------------------------------------------------------------------
NotoSansMalayalamUI-Bold.ttf 马拉雅拉姆文字库(印度)
NotoSansMalayalamUI-Regular.ttf

-------------------------------------------------------------------------------------------------
NotoSansTamilUI-Bold.ttf 泰米尔语字库(印度、斯里兰卡、新加坡)
NotoSansTamilUI-Regular.ttf

-------------------------------------------------------------------------------------------------
NotoSansTeluguUI-Bold.ttf 泰卢固语(印度)
NotoSansTeluguUI-Regular.ttf

-------------------------------------------------------------------------------------------------
NotoSansThaiUI-Bold.ttf 泰语字库
NotoSansThaiUI-Regular.ttf

=======================================================

2.Android手机做热点时,如何获取连过来设备的具体信息?

1、连接过来的设备的信息存放在/data/misc/dhcp/dnsmasq.leases中

2、它的格式是:

/系统id,不需取值/client mac地址/client ip地址/ client device name/加权后mac地址,也不需取值

[plain] view plaincopy
  1. 1357041758 88:00:12:34:56:78 192.168.43.133 android-184cc6c105d7a3b 01:88:00:12:34:56:78

3、参考WifiServie.java的getClientIp()方法,可以自定义这个方法取得device name,具体如下:

[java] view plaincopy
  1. public String getClientDeviceName(String deviceAddress) {//传mac地址进来
  2. enforceAccessPermission();
  3. if (TextUtils.isEmpty(deviceAddress)) {
  4. return null;
  5. }
  6. //读取对应的文件信息
  7. for (String s : readClientList("/data/misc/dhcp/dnsmasq.leases")) {
  8. if (s.indexOf(deviceAddress) != -1) {
  9. String[] fields = s.split(" ");
  10. //校验数据是否破损
  11. if (fields.length > 4) {
  12. //返回第4个栏位
  13. return fields[3];
  14. }
  15. }
  16. }
  17. return null;
  18. }

3.在Fastboot里添加命令

fastboot 是android 默认的一种debug 方法,它的好处是在进入linux kernel 之前
即可操作。
默认fastboot 支持的命令:

[plain] view plaincopy
  1. usage: fastboot [ <option> ] <command>
  2. commands:
  3. update <filename> reflash device from
  4. update.zip
  5. flashall flash boot
  6. + recovery + system
  7. flash <partition> [ <filename> ] write a file to a flash
  8. partition
  9. erase <partition> erase a flash
  10. partition
  11. format <partition> format a flash
  12. partition
  13. getvar <variable> display a
  14. bootloader variable
  15. boot <kernel> [ <ramdisk> ] download and boot kernel
  16. flash:raw boot <kernel> [ <ramdisk> ] create bootimage and flash it
  17. devices list all
  18. connected devices
  19. continue continue
  20. with autoboot
  21. reboot reboot
  22. device normally
  23. reboot-bootloader reboot device
  24. into bootloader
  25. help show this
  26. help message
  27. options:
  28. -w erase userdata and cache (and
  29. format if supported by partition type)
  30. -u do not first erase partition
  31. before formatting
  32. -s <specific device> specify device serial number or path to
  33. device port
  34. -l with "devices", lists device
  35. paths
  36. -p <product> specify product name
  37. -c <cmdline> override kernel commandline
  38. -i <vendor id> specify a custom USB vendor id
  39. -b <base_addr> specify a custom kernel base
  40. address
  41. -n <page size> specify the nand page size.
  42. default: 2048
  43. -S <size>[K|M|G] automatically sparse files
  44. greater than size. 0 to disable

fastboot 提供了扩展的命令符号

[plain] view plaincopy
  1. fastboot oem command args

下面以fastboot oem hello test 来说明如何扩展

(1).在bootable/bootloader/lk/app/mt_boot/fastboot.c
的fastboot_init 函数中添加一个新的register

[cpp] view plaincopy
  1. //第一个参数是命令的名称
  2. //第二个参数是命令的执行函数
  3. //第三个参数是在security IC 中是否还提供此命令
  4. fastboot_register("oem hello", cmd_oem_hello, FALSE);

(2). 实现cmd_oem_hello 函数
void cmd_oem_hello(const char *arg, void *data, unsigned size) {

[cpp] view plaincopy
  1. //注意args 是以command 结束开始,即" args"
  2. if(!strncmp(arg, " OK", strlen(" OK"))){
  3. fastboot_okey("OK");
  4. }else{
  5. fastboot_fail("Not OK");
  6. }
  7. }

(3). 与PC 端交互
您可以使用下面已经定义好的三个函数与PC 端交互

[cpp] view plaincopy
  1. fastboot_okey(const char* result);
  2. fastboot_fail(const char* reason);
  3. fastboot_info(const char* reason);

注意这三个打印字符串的长度都不能超过64-1-4 = 59 个字

4.在任意界面按某个实体键进入某个Activity

有些手机会有附加的功能键,比如拍照实体键,甚至有两端式的,轻按聚焦,深按拍照。那么类似功能是如何在Android手机上实现的呢?

可以修改源码下

frameworks/base/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java文件中的如下方法:

[java] view plaincopy
  1. public long interceptKeyBeforeDispatching(WindowState win, KeyEvent event,int policyFlags)

找到如下代码段:

[java] view plaincopy
  1. else if (keyCode == KeyEvent.KEYCODE_APP_SWITCH) {
  2. if (down && repeatCount == 0 && !keyguardOn) {
  3. showOrHideRecentAppsDialog(RECENT_APPS_BEHAVIOR_SHOW_OR_DISMISS);
  4. }
  5. return -1;
  6. }

在这个else if后面增加相应代码:

[java] view plaincopy
  1. else if (keyCode == KeyEvent.KEYCODE_APP_SWITCH) {
  2. if (down && repeatCount == 0 && !keyguardOn) {
  3. showOrHideRecentAppsDialog(RECENT_APPS_BEHAVIOR_SHOW_OR_DISMISS);
  4. }
  5. return -1;
  6. } //add begin
  7. else if (keyCode == KeyEvent.KEYCODE_XXX) {
  8. if (down && repeatCount == 0 && !keyguardOn) {
  9. mContext.startActivity(new Intent("intent.xxx")
  10. .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
  11. }
  12. return -1;
  13. } // add end

注意:
1、上面写的KeyEvent.KEYCODE_XXX是预设定好的实体键的键值,根据需要来设定即可;

2、startActivity(new Intent("intent.xxx")中的intent.xxx需要根据所启动的activity来写

5.在关机界面添加重启功能

Google原生的Android系统一般是没有“重启”这个选项的。有时候重启也是不可或缺的一个Feature,那么如何在源码环境下添加这个选项呢?

1. 在frameworks\base\core\res\res\values\strings.xml
中添加标签:

[html] view plaincopy
  1. <span style="font-size:14px;"><!--zms add start-->
  2. <!-- label for item that power reboot in phone options dialog -->
  3. <string name="zms_global_action_power_reboot">Reboot</string>
  4. <!--zms add end--></span>

当然这只是英语语系的,需要添加其它语系的标示,把"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行有这样的代码:

[java] view plaincopy
  1. mItems = Lists.newArrayList(
  2. // silent mode
  3. mSilentModeToggle,
  4. // next: airplane mode
  5. mAirplaneModeOn,
  6. // last: power off

在这里,我们添加power reboot 的新的item.
具体这个mItems 更新为如下:

[java] view plaincopy
  1. mItems = Lists.newArrayList(
  2. // silent mode
  3. mSilentModeToggle,
  4. // next: airplane mode
  5. mAirplaneModeOn,
  6. // last: power off
  7. new SinglePressAction(
  8. com.android.internal.R.drawable.ic_lock_power_off,
  9. R.string.global_action_power_off) {
  10. public void onPress() {
  11. // shutdown by making sure radio and power are handled
  12. accordingly.
  13. ShutdownThread.shutdown(mContext, true);
  14. }
  15. public boolean showDuringKeyguard() {
  16. return true;
  17. }
  18. public boolean showBeforeProvisioning() {
  19. return true;
  20. }
  21. }//zms add start
  22. ,
  23. new SinglePressAction(
  24. com.android.internal.R.drawable.zms_ic_lock_power_reboot,
  25. R.string.zms_global_action_power_reboot) {
  26. public void onPress() {
  27. // reboot by making sure radio and power are handled
  28. accordingly.
  29. ShutdownThread.reboot(mContext, null, true);
  30. }
  31. public boolean showDuringKeyguard() {
  32. return true;
  33. }
  34. public boolean showBeforeProvisioning() {
  35. return true;
  36. }
  37. }
  38. //zms add end.
  39. );

经过这样的添加/修改后,这项feature 即可运行。
注意如果测试的话,因为有修改framework 中的文件,最好new 一下整个工程。
另外还需要修改一下ShutdownThread.java 中的那个dialog 显示描述,不然将依旧看到“关机”的信息。

位置:frameworks/base/services/java/com/android/server/power/ShutdownThread.java

不同版本的代码位置可能有所差别,可以在根目录下find一下:

[plain] view plaincopy
  1. find -name ShutdownThread.java

如下:

[java] view plaincopy
  1. sConfirmDialog = new AlertDialog.Builder(context)
  2. .setTitle((mReboot && !mRebootSafeMode)
  3. ? com.android.internal.R.string.reboot_title
  4. : (mRebootSafeMode
  5. ? com.android.internal.R.string.reboot_safemode_title
  6. : com.android.internal.R.string.power_off))
  7. .setMessage(resourceId)
  8. .setPositiveButton(com.android.internal.R.string.yes, new DialogInterface.OnClickListener() {
  9. public void onClick(DialogInterface dialog, int which) {
  10. beginShutdownSequence(context);
  11. if (sConfirmDialog != null) {
  12. sConfirmDialog = null;
  13. }
  14. }
  15. })
  16. .setNegativeButton(com.android.internal.R.string.no, new DialogInterface.OnClickListener() {
  17. public void onClick(DialogInterface dialog, int which) {
  18. synchronized (sIsStartedGuard) {
  19. sIsStarted = false;
  20. }
  21. if (sConfirmDialog != null) {
  22. sConfirmDialog = null;
  23. }
  24. }
  25. })
  26. .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为显示,默认隐藏
[cpp] view plaincopy
  1. { "app.launcher.start", AID_SYSTEM, 0},
  2. "app.phonesky.show", AID_SYSTEM, 0}, //Add By zj
  3. { "cdma.",        AID_RADIO,    0 },    //Add by gfzhu VIA
2.在设置的开发者选项中实现对应的接口:
文件路径:packages/apps/Settings/src/com/android/settings/DevelopmentSettings.java
①声明和初始化:
[java] view plaincopy
  1. private static final String SHOW_PHONESKY = "show_phonesky";
  2. private CheckBoxPreference mShowPhonesky;
  3. mShowPhonesky = findAndInitCheckboxPref(SHOW_PHONESKY);
②CheckBox的逻辑:
[java] view plaincopy
  1. (BatteryManager.BATTERY_PLUGGED_AC | BatteryManager.BATTERY_PLUGGED_USB) : 0);
  2. +      } else if (preference == mShowPhonesky) { // ZJ Add
  3. +           if(mShowPhonesky.isChecked())
  4. +           {
  5. +              SystemProperties.set("app.phonesky.show","1");
  6. +           }else{
  7. +              SystemProperties.set("app.phonesky.show","0");
  8. +           }
  9. } else if (preference == mBtHciSnoopLog) {
③增加一个Preference:
packages/apps/Settings/res/xml/development_prefs.xml
[html] view plaincopy
  1. android:targetClass="com.android.settings.SetFullBackupPassword" />
  2. </PreferenceScreen>
  3. +       <CheckBoxPreference
  4. +        android:key="show_phonesky"
  5. +        android:title="@string/show_phonesky"
  6. +        />
  7. <CheckBoxPreference
④添加对应语言的string字符:
[html] view plaincopy
  1. <string name="show_phonesky">Show Google Play Store</string>
⑤设置中新增一个监听,初始化Checkbox的逻辑:
packages/apps/Settings/src/com/android/settings/BootReceiver.java
内容如下:
[java] view plaincopy
  1. package com.android.settings;
  2. import android.content.BroadcastReceiver;
  3. import android.content.Context;
  4. import android.content.Intent;
  5. import android.content.SharedPreferences;
  6. import android.os.SystemClock;
  7. import android.util.Log;
  8. import android.os.SystemProperties;
  9. public class BootReceiver extends BroadcastReceiver{
  10. @Override
  11. public void onReceive(Context arg0, Intent arg1) {
  12. // TODO Auto-generated method stub
  13. String action = arg1.getAction();
  14. if(action.equals(Intent.ACTION_BOOT_COMPLETED))
  15. {
  16. SharedPreferences shared = arg0.getSharedPreferences("com.android.settings_preferences", Context.MODE_PRIVATE);
  17. boolean show_phonesky = shared.getBoolean("show_phonesky", false);
  18. if(show_phonesky){
  19. SystemProperties.set("app.phonesky.show","1");
  20. }else{
  21. SystemProperties.set("app.phonesky.show","0");
  22. }
  23. }
  24. }
  25. }
⑥在Settings的AndroidManifest文件中添加BroadcastReceiver的权限和声明:
[html] view plaincopy
  1. <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
  2. <receiver android:name=".BootReceiver">
  3. <intent-filter>
  4. <action android:name="android.intent.action.BOOT_COMPLETED" />
  5. <category android:name="android.intent.category.DEFAULT" />
  6. </intent-filter>
  7. </receiver>
3.在init.rc中添加对应的服务和触发条件:
路径:mediatek/config/esky27_tb_ccn_mlc_kk/init.rc
[plain] view plaincopy
  1. +# ZJ Add START
  2. +#Hide or Show Google Play Dynamicly
  3. +#disabled:服务不会自动运行,必须显式地通过服务器来启动。
  4. +#oneshot:当此服务退出时不会自动重启。
  5. +service hidePhonesky /system/bin/hidePhonesky
  6. +       disabled
  7. +       oneshot
  8. +service showPhonesky /system/bin/showPhonesky
  9. +       disabled
  10. +       oneshot
  11. +#on property:sys.boot_completed=1
  12. +#   start renamePhonesky
  13. +on property:app.phonesky.show=1
  14. +   start showPhonesky
  15. +on property:app.phonesky.show=0
  16. +   start hidePhonesky
  17. +# ZJ Add END
4.隐藏和显示应用的脚本:
隐藏应用:vendor/ThirdParty/App/dte/hidePhonesky
内容:

[plain] view plaincopy
  1. #!/system/bin/sh
  2. #!/system/bin/busybox
  3. mount -o remount,rw /system;
  4. mv /system/priv-app/Phonesky.apk /system/priv-app/Phonesky.bak
显示应用:vendor/ThirdParty/App/dte/showPhonesky
内容:

[java] view plaincopy
  1. #!/system/bin/sh
  2. #!/system/bin/busybox
  3. mount -o remount,rw /system;
  4. mv /system/priv-app/Phonesky.bak /system/priv-app/Phonesky.apk
5.拷贝脚本到system/bin目录下:
参考以下格式添加到对应的mk文件:
[plain] view plaincopy
  1. +#添加重命名GooglePlay脚本
  2. +PRODUCT_COPY_FILES += \
  3. +       vendor/ThirdParty/App/dte/hidePhonesky:system/bin/hidePhonesky \
  4. +       vendor/ThirdParty/App/dte/showPhonesky:system/bin/showPhonesky \
  5. +       vendor/ThirdParty/App/dte/Phonesky.bak:system/priv-app/Phonesky.bak
7.修改链接电脑时的“总线已报告设备描述”显示名称

在:Android USB Gadget Driver中进行修改

[cpp] view plaincopy
  1. static const char longname[] = "Gadget Android";
  2. /* Default vendor and product IDs, overridden by userspace */
  3. #define VENDOR_ID       0x0BB4
  4. #define PRODUCT_ID      0x0001
  5. /* Default manufacturer and product string , overridden by userspace */
  6. // 制造商
  7. #define MANUFACTURER_STRING "MediaTek"
  8. // 设备描述,可以在“总线已报告设备描述”中看到
  9. #define PRODUCT_STRING "MTP"
  10. #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 里面的

[plain] view plaincopy
  1. ifeq ($(TARGET_BUILD_VARIANT),user)
  2. BOARD_SEPOLICY_IGNORE+=external/sepolicy/su.te
  3. else
  4. BOARD_SEPOLICY_IGNORE+=external/sepolicy/su_user.te
  5. endif

更新成:

[java] view plaincopy
  1. ifeq ($(TARGET_BUILD_VARIANT),user)
  2. BOARD_SEPOLICY_IGNORE+=external/sepolicy/su_user.te
  3. else
  4. BOARD_SEPOLICY_IGNORE+=external/sepolicy/su_user.te
  5. 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

[java] view plaincopy
  1. boolean selectNetwork(int netId) {
  2. if (VDBG) localLog("selectNetwork", netId);
  3. if (netId == INVALID_NETWORK_ID) return false;
  4. // Reset the priority of each network at start or if it goes too high.
  5. if (mLastPriority == -1 || mLastPriority > 1000000) {
  6. Xlog.d(TAG, "Need to reset the priority, mLastPriority:" + mLastPriority);
  7. for(WifiConfiguration config : mConfiguredNetworks.values()) {
  8. if (config.networkId != INVALID_NETWORK_ID) {
  9. config.priority = 0;
  10. addOrUpdateNetworkNative(config);
  11. }
  12. }
  13. mLastPriority = 0;
  14. }
  15. // Set to the highest priority and save the configuration.
  16. WifiConfiguration config = new WifiConfiguration();
  17. config.networkId = netId;
  18. config.priority = ++mLastPriority;
  19. addOrUpdateNetworkNative(config);
  20. mWifiNative.saveConfig();
  21. /* Enable the given network while disabling all other networks */
  22. enableNetworkWithoutBroadcast(netId, true);
  23. /* Avoid saving the config & sending a broadcast to prevent settings
  24. * from displaying a disabled list of networks */
  25. return true;
  26. }

有时候,我们会忘记已连接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 的结果:

[java] view plaincopy
  1. diff --git a/packages/apps/Settings/src/com/android/settings/applications/ApplicationsState.java b/packages/apps/Settings/src/com/android/settings/applications/ApplicationsState.java
  2. index e87d7cf..3f1a507 100644 (file)
  3. --- a/packages/apps/Settings/src/com/android/settings/applications/ApplicationsState.java
  4. +++ b/packages/apps/Settings/src/com/android/settings/applications/ApplicationsState.java
  5. @@ -33,6 +33,8 @@ import java.util.HashMap;
  6. import java.util.List;
  7. import java.util.regex.Pattern;
  8. +import android.os.TCToolManager;
  9. +
  10. /**
  11. * Keeps track of information about all installed applications, lazy-loading
  12. * as needed.
  13. @@ -42,6 +44,8 @@ public class ApplicationsState {
  14. static final boolean DEBUG = false;
  15. static final boolean DEBUG_LOCKING = false;
  16. +       final TCToolManager mTCTool;
  17. +
  18. public static interface Callbacks {
  19. public void onRunningStateChanged(boolean running);
  20. public void onPackageListChanged();
  21. @@ -404,6 +408,8 @@ public class ApplicationsState {
  22. mThread.start();
  23. mBackgroundHandler = new BackgroundHandler(mThread.getLooper());
  24. +               mTCTool = (TCToolManager)mContext.getSystemService(Context.TCHIP_TOOL_SERVICE);
  25. +
  26. // Only the owner can see all apps.
  27. if (UserHandle.myUserId() == 0) {
  28. mRetrieveFlags = PackageManager.GET_UNINSTALLED_PACKAGES |
  29. @@ -548,6 +554,13 @@ public class ApplicationsState {
  30. if (DEBUG_LOCKING) Log.v(TAG, "rebuild acquired lock");
  31. AppEntry entry = getEntryLocked(info);
  32. entry.ensureLabel(mContext);
  33. +
  34. +                        if (mTCTool.isHide(info.packageName)) {
  35. +                            if(TCToolManager.DEBUG)
  36. +                                TCToolManager.Log("hide app:" + info.loadLabel(mPm) + ":" + info.packageName);
  37. +                            continue;
  38. +                        }
  39. +
  40. if (DEBUG) Log.i(TAG, "Using " + info.packageName + ": " + entry);
  41. filteredApps.add(entry);
  42. if (DEBUG_LOCKING) Log.v(TAG, "rebuild releasing lock");

然后添加以下三个文件到指定路径:

frameworks/base/core/java/android/os/ITCToolService.aidl:

[java] view plaincopy
  1. /**
  2. * Copyright (c) 2007, 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. package android.os;
  17. import android.content.Context;
  18. /** {@hide} */
  19. interface ITCToolService
  20. {
  21. String getVersion();
  22. boolean isHide(String name);
  23. }

frameworks/base/core/java/android/os/TCToolManager.java:

[java] view plaincopy
  1. 1 /*
  2. 2  * Copyright (C) 2006 The Android Open Source Project
  3. 3  *
  4. 4  * Licensed under the Apache License, Version 2.0 (the "License");
  5. 5  * you may not use this file except in compliance with the License.
  6. 6  * You may obtain a copy of the License at
  7. 7  *
  8. 8  *      http://www.apache.org/licenses/LICENSE-2.0
  9. 9  *
  10. 10  * Unless required by applicable law or agreed to in writing, software
  11. 11  * distributed under the License is distributed on an "AS IS" BASIS,
  12. 12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. 13  * See the License for the specific language governing permissions and
  14. 14  * limitations under the License.
  15. 15  */
  16. 16
  17. 17 package android.os;
  18. 18
  19. 19 import android.util.Log;
  20. 20
  21. 21
  22. 22
  23. 23 public class TCToolManager
  24. 24 {
  25. 25     private static final String TAG = "TCToolManager";
  26. 26     public static final boolean DEBUG = true;
  27. 27
  28. 28     ITCToolService mService;
  29. 29
  30. 30     /** @hide */
  31. 31     public TCToolManager(ITCToolService service)
  32. 32     {
  33. 33         mService = service;
  34. 34         Log.d(TAG, "version: " + getVersion());
  35. 35     }
  36. 36
  37. 37     public String getVersion() {
  38. 38
  39. 39         try {
  40. 40             return mService.getVersion();
  41. 41         } catch (RemoteException e) {
  42. 42         }
  43. 43
  44. 44         return null;
  45. 45     }
  46. 46
  47. 47     public boolean isHide(String name) {
  48. 48         try {
  49. 49             return mService.isHide(name);
  50. 50         } catch (RemoteException e) {
  51. 51         }
  52. 52
  53. 53         return false;
  54. 54     }
  55. 55
  56. 56     // debug log out
  57. 57     static public void Log(String log) {
  58. 58         // TODO Auto-generated method stub
  59. 59         Log.d(TAG, log);
  60. 60     }
  61. 61 }

frameworks/base/services/java/com/android/server/TCToolService.java:

[java] view plaincopy
  1. 1 /*
  2. 2  * Copyright (C) 2008 The Android Open Source Project
  3. 3  *
  4. 4  * Licensed under the Apache License, Version 2.0 (the "License");
  5. 5  * you may not use this file except in compliance with the License.
  6. 6  * You may obtain a copy of the License at
  7. 7  *
  8. 8  *      http://www.apache.org/licenses/LICENSE-2.0
  9. 9  *
  10. 10  * Unless required by applicable law or agreed to in writing, software
  11. 11  * distributed under the License is distributed on an "AS IS" BASIS,
  12. 12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. 13  * See the License for the specific language governing permissions and
  14. 14  * limitations under the License.
  15. 15  */
  16. 16
  17. 17 package com.android.server;
  18. 18
  19. 19 import android.content.Context;
  20. 20 import android.content.Intent;
  21. 21 import android.content.SharedPreferences;
  22. 22 import android.os.ITCToolService;
  23. 23 import android.os.storage.StorageEventListener;
  24. 24 import android.os.storage.StorageManager;
  25. 25 import android.preference.PreferenceManager;
  26. 26 import android.util.Log;
  27. 27 import android.util.Slog;
  28. 28
  29. 29 import java.io.BufferedReader;
  30. 30 import java.io.DataOutputStream;
  31. 31 import java.io.File;
  32. 32 import java.io.FileInputStream;
  33. 33 import java.io.FileNotFoundException;
  34. 34 import java.io.FileOutputStream;
  35. 35 import java.io.IOException;
  36. 36 import java.io.InputStreamReader;
  37. 37 import java.util.ArrayList;
  38. 38 import java.util.Calendar;
  39. 39
  40. 40 public class TCToolService extends ITCToolService.Stub {
  41. 41     private static final String TAG = "TCToolService";
  42. 42     private static final String VERSION = "T-CHIP tool V1.0.1";
  43. 43     private Context mContext;
  44. 44     private final File mToolDir;
  45. 45     private boolean inited = false;
  46. 46
  47. 47     private static final boolean DEBUG = true;
  48. 48
  49. 49     private static final String FILE = "hideapk.txt";
  50. 50
  51. 51     private static final String EXTSD_PATH = "/mnt/external_sd";
  52. 52
  53. 53     private static final String SYS_PATH = "/system/usr/data";
  54. 54
  55. 55     private ArrayList<String> mHideList = new ArrayList<String>();
  56. 56
  57. 57     private static final Object sLock = new Object();
  58. 58     private StorageManager mStorageManager = null;
  59. 59
  60. 60     public String getVersion() {
  61. 61         return VERSION;
  62. 62     }
  63. 63
  64. 64     TCToolService(Context context, File path) {
  65. 65         mContext = context;
  66. 66         mToolDir = path;
  67. 67
  68. 68         initHideLocked(false);
  69. 69     }
  70. 70
  71. 71     private void initHideLocked(boolean forceUpdate) {
  72. 72         synchronized (sLock) {
  73. 73             if (!inited || forceUpdate) {
  74. 74                 inited = true;
  75. 75                 initHide(forceUpdate);
  76. 76             }
  77. 77         }
  78. 78     }
  79. 79
  80. 80     private void initHide(boolean forceUpdate) {
  81. 81         File hideFile = null;
  82. 82         File outFile = new File(mToolDir, FILE);
  83. 83         boolean bWriteCfg = true;
  84. 84
  85. 85         if (!mToolDir.exists()) {
  86. 86             mToolDir.mkdirs();
  87. 87         }
  88. 88
  89. 89         if (outFile.exists() && !forceUpdate) {
  90. 90             bWriteCfg = false;
  91. 91         } else if ((hideFile = new File(EXTSD_PATH + "/" + FILE)).exists()) {
  92. 92             setSDHideState(true);
  93. 93             setSDHideDate(hideFile.lastModified());
  94. 94             if (DEBUG)
  95. 95                 Log.d(TAG, "Set bSDHideExists");
  96. 96         }
  97. 97
  98. 98         if (DEBUG && null != hideFile)
  99. 99                 Log.d(TAG, "Load hideapk.txt from " + hideFile.getPath());
  100. 100
  101. 101         mHideList.clear();
  102. 102         readHideCfg(new File(SYS_PATH + "/" + FILE), mHideList);
  103. 103         readHideCfg(hideFile, mHideList);
  104. 104
  105. 105         if (bWriteCfg && !mHideList.isEmpty()) {
  106. 106             if (DEBUG)
  107. 107                 Log.d(TAG, "Save hideapk.txt to app file");
  108. 108             writeHideCfg(outFile, mHideList);
  109. 109         }
  110. 110
  111. 111         if (null == mStorageManager) {
  112. 112             mStorageManager = (StorageManager) mContext.getSystemService(Context.STORAGE_SERVICE);
  113. 113             mStorageManager.registerListener(mStorageListener);
  114. 114         }
  115. 115     }
  116. 116
  117. 117     private void writeHideCfg(File hideFile, ArrayList<String> hidelist) {
  118. 118         DataOutputStream out = null;
  119. 119
  120. 120         try {
  121. 121             out = new DataOutputStream(new FileOutputStream(hideFile));
  122. 122             for (String hide : hidelist) {
  123. 123                 // out.writeUTF(hide + "\n");
  124. 124                 out.writeBytes(hide + "\n");
  125. 125             }
  126. 126             out.flush();
  127. 127         } catch (FileNotFoundException e) {
  128. 128             // Ignore
  129. 129             e.printStackTrace();
  130. 130         } catch (IOException e) {
  131. 131             e.printStackTrace();
  132. 132             // noinspection ResultOfMethodCallIgnored
  133. 133             hideFile.delete();
  134. 134         } finally {
  135. 135             if (out != null) {
  136. 136                 try {
  137. 137                     out.close();
  138. 138                 } catch (IOException e) {
  139. 139                     // Ignore
  140. 140                 }
  141. 141             }
  142. 142         }
  143. 143     }
  144. 144
  145. 145     private void readHideCfg(File hideFile, ArrayList<String> hidelist) {
  146. 146         if (hideFile == null || !hideFile.exists())
  147. 147             return;
  148. 148
  149. 149         try {
  150. 150             FileInputStream inStream = new FileInputStream(hideFile);
  151. 151
  152. 152             if (inStream != null) {
  153. 153                 BufferedReader rbf = new BufferedReader(new InputStreamReader(inStream));
  154. 154
  155. 155                 String linebuf = rbf.readLine();
  156. 156                 while (linebuf != null) {
  157. 157                     linebuf = linebuf.trim();
  158. 158                     if (!linebuf.startsWith("#", 0) && linebuf.length() != 0) {
  159. 159                         if (DEBUG)
  160. 160                             Log.d(TAG, "readHideCfg: " + linebuf);
  161. 161                         hidelist.add(linebuf);
  162. 162                     }
  163. 163                     linebuf = rbf.readLine();
  164. 164                 }
  165. 165                 return;
  166. 166             }
  167. 167         } catch (FileNotFoundException e) {
  168. 168             Log.e(TAG, "readHideCfg: " + "File is not found");
  169. 169         } catch (Exception e) {
  170. 170             Log.e(TAG, "Read hide config error");
  171. 171             e.printStackTrace();
  172. 172         }
  173. 173     }
  174. 174
  175. 175     public boolean isHide(String name) {
  176. 176         synchronized (sLock) {
  177. 177             if (mHideList.contains(name)) {
  178. 178                 return true;
  179. 179             } else {
  180. 180                 return false;
  181. 181             }
  182. 182         }
  183. 183     }
  184. 184
  185. 185     /*
  186. 186      * change condition: 1. original not exists but exists currently 2. current
  187. 187      * is newer than original 3. exclude original exists but not exists
  188. 188      * currently, keep original hide list
  189. 189      */
  190. 190     private boolean isSDHideFileChange() {
  191. 191         File hideFile = new File(EXTSD_PATH + "/" + FILE);
  192. 192
  193. 193         if (DEBUG)
  194. 194             Log.d(TAG, "isSDHideFileChange: new state-->" + hideFile.exists() + ", old state-->"
  195. 195                     + getSDHideState());
  196. 196
  197. 197         if (hideFile.exists()) {
  198. 198             /* original not exists but exists currently */
  199. 199             if (!getSDHideState())
  200. 200                 return true;
  201. 201
  202. 202             if (DEBUG) {
  203. 203                 Calendar cal = Calendar.getInstance();
  204. 204                 cal.setTimeInMillis(hideFile.lastModified());
  205. 205                 Log.d(TAG, "isSDHideFileChange: new date-->" + hideFile.lastModified() + "="
  206. 206                         + cal.getTime().toLocaleString());
  207. 207                 cal.setTimeInMillis(getSDHideDate());
  208. 208                 Log.d(TAG, "isSDHideFileChange: old date-->" + getSDHideDate() + "="
  209. 209                         + cal.getTime().toLocaleString());
  210. 210             }
  211. 211             /* current is newer than original */
  212. 212             if ((hideFile.lastModified() > getSDHideDate())) {
  213. 213                 return true;
  214. 214             }
  215. 215         }
  216. 216
  217. 217         return false;
  218. 218     }
  219. 219
  220. 220     private void setSDHideState(boolean state) {
  221. 221         SharedPreferences preferences = PreferenceManager
  222. 222                 .getDefaultSharedPreferences(mContext);
  223. 223         SharedPreferences.Editor editor = preferences.edit();
  224. 224
  225. 225         editor.putBoolean("sd_hide_state", state);
  226. 226         editor.commit();
  227. 227     }
  228. 228
  229. 229     private boolean getSDHideState() {
  230. 230         SharedPreferences preferences = PreferenceManager
  231. 231                 .getDefaultSharedPreferences(mContext);
  232. 232
  233. 233         return preferences.getBoolean("sd_hide_state", false);
  234. 234
  235. 235     }
  236. 236
  237. 237     private void setSDHideDate(long date) {
  238. 238         SharedPreferences preferences = PreferenceManager
  239. 239                 .getDefaultSharedPreferences(mContext);
  240. 240         SharedPreferences.Editor editor = preferences.edit();
  241. 241
  242. 242         editor.putLong("sd_hide_date", date);
  243. 243         editor.commit();
  244. 244     }
  245. 245
  246. 246     private long getSDHideDate() {
  247. 247         SharedPreferences preferences = PreferenceManager
  248. 248                 .getDefaultSharedPreferences(mContext);
  249. 249
  250. 250         return preferences.getLong("sd_hide_date", -1);
  251. 251
  252. 252     }
  253. 253
  254. 254     StorageEventListener mStorageListener = new StorageEventListener() {
  255. 255
  256. 256         @Override
  257. 257         public void onStorageStateChanged(String path, String oldState, String newState) {
  258. 258             if (DEBUG)
  259. 259                 Log.d(TAG, "onStorageStateChanged: " + path + "--" + newState);
  260. 260             if (newState.equals("mounted") && path.equals(EXTSD_PATH)) {
  261. 261                 if (isSDHideFileChange()) {
  262. 262                     initHideLocked(true);
  263. 263
  264. 264                     // TODO send Broadcast ACTION_TCTOOL_SDHIDE_CHANGED
  265. 265                     Intent it = new Intent(Intent.ACTION_TCTOOL_SDHIDE_CHANGED);
  266. 266                     mContext.sendBroadcast(it);
  267. 267                     if (DEBUG)
  268. 268                         Log.d(TAG, "Send sendBroadcast: ACTION_TCTOOL_SDHIDE_CHANGED");
  269. 269                 }
  270. 270             }
  271. 271         }
  272. 272     };
  273. 273 }

Android 底层知识拾零相关推荐

  1. Android 底层知识拾零,字节跳动Android高级工程师

    NotoSansTeluguUI-Bold.ttf 泰卢固语(印度) NotoSansTeluguUI-Regular.ttf ------------------------------------ ...

  2. Android 底层知识拾零,app架构升级

    NotoSansTeluguUI-Regular.ttf ----------------------------------------------------------------------- ...

  3. Android 底层知识拾零,android原生开发框架

    ------------------------------------------------------------------------------------------------- pa ...

  4. 写给Android App开发人员看的Android底层知识合集(1-8)

    写给Android App开发人员看的Android底层知识合集(1-8) 转自包老师:http://www.cnblogs.com/Jax/p/6864103.html 写给Android App开 ...

  5. Android 底层知识-SMMU

    1.首先了解下MMU MMU是Memory Management Unit的缩写,中文名是内存管理单元.它是一种负责处理中央处理器(CPU)的内存访问请求的计算机硬件.它的功能包括虚拟地址到物理地址的 ...

  6. 【 karle 专栏 】Android 初探底层知识系列

    这一系列底层知识基于Android 6.0.1版本. 概述 在我还是菜鸟的时候,有很多技术都不明白,也找不到答案,比如,apk是如何安装的?资源是怎么加载的?再比如,AIDL,只听未用过.四大组件也是 ...

  7. linux内核epub,Android底层开发技术实战详解——内核、移植和驱动(第2版)[EPUB][MOBI][AZW3][42.33MB]...

    内容简介 本书从底层原理开始讲起,结合真实的案例向读者详细介绍了Android内核.移植和驱动开发的整个流程.全书分为21章,依次讲解驱动移植的必要性, Goldfish.OMAP内核和驱动解析,显示 ...

  8. Android底层和中间层共同学习系列之android键盘映射

     http://blog.csdn.net/hongjiujing/article/details/5016730 Android底层和中间层共同学习系列之android键盘映射         ...

  9. Android 基础知识+app测试权限问题

    Android 基础知识(权限篇)** 前言 ​ Android是一个开源的,基于Linux的移动设备操作系统,主要用于移动设备,如智能手机和平板电脑.Android是由谷歌及其他公司带领的开放手机联 ...

最新文章

  1. AutoCAD 2D与3D大师班学习教程 AutoCAD 2D and 3D Masterclass
  2. 性能优化指南(5000 字小结)
  3. Android --- 解决 cannot connect to daemon at tcp:5037: cannot connect to 127.0.0.1:5037: 由于目标计算机积极拒绝,无
  4. 数据库-优化-Limit查询的优化
  5. [剑指offer]面试题8:旋转数组的最小数字
  6. P4321-随机漫游【状压dp,数学期望,高斯消元】
  7. 第二十一章 刘备脱险
  8. Serverless在大规模数据处理的实践
  9. nodejs missing script: dev_nodejs深入学习系列之v8基础篇
  10. [学习笔记] PHP回调函数的实现方法 [转]
  11. 微软更新Azure SQL将可根据重要性工作进行重整顺序
  12. python中初始化方法_Python中类的初始化特殊方法
  13. double float区别 java,float和double有什么区别?
  14. mipi协议_Cadence发布业界首款面向多协议PHY的验证IP产品
  15. 《ETL原理及应用》学习笔记 ·001【ETL介绍】
  16. [数位dp] Jzoj P4239 光棍
  17. python集合常用方法_python基础-集合set的常用方法
  18. CRM客户关系管理系统HR人事OA系统APP源码
  19. Python爬虫入门教程:豆瓣Top电影爬取
  20. 彻底解决SysFader:IEXPLORE.EXE应用程序错误

热门文章

  1. UniVL: A Unifified Video and Language Pre-Training Model for Multimodal Understanding and Generation
  2. (三)基于Multisim的超外差接收系统:中频放大器的设计
  3. 周易六十四卦——天风姤卦
  4. python权限管理设置_python权限管理框架
  5. linux系统之系统修复
  6. 【Android View】写一个蛛网评分控件
  7. UE4对电脑配置的要求
  8. 【html+js+jquery简单表单验证和删查】
  9. 判断奇偶性。输入一个整数n,判断n是奇函数还是偶数,若为奇函数输出“奇数”,若为偶函数输出“偶数”
  10. 基于顺序表的图书管理系统(C语言)