使用内部(com.android.internal)和隐藏(@hide)API ——介绍

本文探讨了Android中两类不可见API:内部API与隐藏API的区别及使用风险,并提供了不借助反射机制调用这些API的方法。

http://blog.youkuaiyun.com/wxlinwzl/article/details/29925073




android源代码分析一:使用内部(com.android.internal)和隐藏(@hide)API ——介绍

分类: Android系统 Android应用开发 架构 786人阅读 评论(0) 收藏 举报

目录(?)[+]

Android有两类API在SDK中不能使用。

第一类就是位于包com.android.internal的API,我将这些API称为内部API。第二类API就是用@hide标记的类和函数,虽然严格说这不是一个API而是一系列隐藏API的集合,我仍然假定这是一个API,称为隐藏API。

隐藏API的例子

你阅读android的源码,就会发现有些常量、函数和类标记为@hide.

这里有一个隐藏常量的例子,来自WifiManager(source code of API Level 10).


另外一个例子是隐藏函数setWifiAppEnabled,来自WifiManager(source code of API Level 10).


所以您只要看到@hide属性,这就是一个隐藏API。

内部API和隐藏API的不同

隐藏API隐藏是为了防止开发人员使用SDK中未完成或者未稳定(接口和架构方面看)的部分。比如,Bluetooth API在API Level 5(android 2.0)之前就存在,但在API Level 3和4(android 1.5和1.6)中使用@hide隐藏起来了。当该API稳定下来,google的开发人员移除@hide属性,在API Level 5中就有Bluetooth API了。还有很多东西在Level 4和5之间发生了变化。如果程序依赖于某些隐藏API,可能会在新版本的Android OS上运行出现问题。

而内部API则不计划对外开放。这是android的内部餐厅,开发人员可以视为黑盒子。这里面的东西同样可能发生改变。同样的,如果您的程序依赖于内部API,在新的Android发布后,可能遇到麻烦。

下面总结它们之间的不同:

[html]  view plain copy
  1. 隐藏API = 正在开发中;  
  2.   
  3. 内部API = 黑盒  

内部和隐藏API的编译时和运行时对比

当您使用Android SDK进行开发时,会引用一个非常重要的jar文件android.jar。它位于Android SDK的平台目录SDK_DIR/platforms/platform-X/android.jar(其中X为API Level,可以是5或者10或其它的数字)。在android.jar中,com.android.internal中所有的类移除了,同样的,所有标记为@hide的类、枚举、字段、方法也移除了。

但是当您在设备中运行应用程序时,加载的是framework.jar(大约等价于android.jar),它没有被裁减,包含所有的内部类和隐藏API。所以您可以使用反射机制来访问隐藏API和内部API(当然,这种方法使用起来不太方便,下面我将介绍不使用反射机制访问这些API的方法)。

关于内部API还有一些特别。Eclipse的ADT插件增加了一条额外规则,禁止使用来自com.android.internal包的任何东西。所以,即使您使用了原始(未裁减)的android.jar,也不容易在eclipse中使用内部API。

您可以自己检查一下。在eclipse中创建一个新的Android工程(或使用现有的),查看它的引用库(右键点击工程,Properties –> Java Build Path –> Libraries)。



重要总结:在SDK中内部API和隐藏API处理方式基本上相同(都从android.jar中移除),但内部API在Eclipse ADT插件中显示禁止了。

不通过反射机制使用内部API和隐藏API

本系列文章的终极目标是给程序员不用反射而使用内部API和隐藏API的方法。如果您完成了在后面文章中的所有步骤,您将可以向象使用官方API那样使用内部API和隐藏API,没有必要使用反射。

但是如果您使用这些非公开的API,必须意识到存在一个巨大的风险。首先不能保证这些API在Android OS升级后不会变化,其次也不能保证在不同厂家的不同设备上有一致的行为。这完全取决您自己。

有以下三种场景:

  1. 开启内部API和隐藏API(场景A)
  2. 仅开启隐藏API(场景B)
  3. 仅开启内部API(场景C)

场景A是场景B和C的综合。场景B是最简单的(不需要修改eclipse ADT插件)。

场景A:阅读1,2,3,4,5

场景B:阅读1,2,3,5

场景C:阅读1,2,3,4,5

参考资料:

使用内部(com.android.internal)和隐藏(@hide)API[第1部分,介绍]


package android.os; /** * @hide Only for use within the system server */ interface IUuidService { String getOrCreatePersistentUuid(); } package com.android.server.os; import android.content.Context; import android.os.IUuidService; import android.os.UuidManager; import android.util.Slog; import com.android.internal.os.UuidPersistStorage; public class UuidService extends IUuidService.Stub { private static final String TAG = "UuidService"; private final Context mContext; public UuidService(Context context) { mContext = context; Slog.i(TAG, "UuidService started"); } @Override public String getOrCreatePersistentUuid() { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.READ_DEVICE_UUID, null); return UuidPersistStorage.getOrCreatePersistentUuid(); } } // frameworks/base/core/java/android/os/UuidManager.java package android.os; import android.annotation.NonNull; import android.annotation.SystemApi; import android.os.IBinder; import android.os.Parcel; import android.os.RemoteException; import android.os.ServiceManager; import android.util.Log; /** * Provides access to a persistent unique identifier for the device. * * @hide This class may be promoted to @SystemApi in future. */ @SystemApi public class UuidManager { private static final String TAG = "UuidManager"; private static final String SERVICE_NAME = "uuid"; // 必须与 IUuidService.aidl 中的方法顺序一致 private static final int TRANSACTION_getOrCreatePersistentUuid = IBinder.FIRST_CALL_TRANSACTION + 0; private final IBinder mBinder; /** * Private constructor to prevent direct instantiation. * Use getSystemService(UuidManager.class) instead. */ /* package */ UuidManager() { mBinder = ServiceManager.getService(SERVICE_NAME); if (mBinder == null) { Log.e(TAG, "Failed to get UUID service from ServiceManager"); } } /** * Factory method for creating UuidManager instances. * Used by SystemServiceRegistry. * * @hide */ @SystemApi(client = SystemApi.Client.SYSTEM_SERVER) public static UuidManager create() { return new UuidManager(); } /** * Gets or creates a persistent UUID for this device. * * @return the UUID string, never null * @throws IllegalStateException if service is unavailable */ @NonNull public String getOrCreatePersistentUuid() { if (mBinder == null) { throw new IllegalStateException("UUID service not available"); } Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); try { // 必须写入接口标识符(由 AIDL 自动生成) data.writeInterfaceToken("android.os.IUuidService"); // 发起调用(transaction code 由 AIDL 分配) mBinder.transact(TRANSACTION_getOrCreatePersistentUuid, data, reply, 0); // 检查异常 reply.readException(); // 读取返回值 String uuid = reply.readString(); return uuid != null ? uuid : "unknown"; } catch (RemoteException e) { Log.e(TAG, "Remote exception while getting UUID", e); throw e.rethrowFromSystemServer(); } finally { data.recycle(); reply.recycle(); } } } package com.android.internal.os; import android.util.Log; import android.annotation.NonNull; import java.io.*; import java.util.UUID; public final class UuidPersistStorage { private static final String TAG = "UuidPersistStorage"; private static final String UUID_FILE_PATH = "/persist/device_uuid"; private static String sCachedUuid; private static final Object sLock = new Object(); public static @NonNull String getOrCreatePersistentUuid() { if (sCachedUuid != null) { return sCachedUuid; } synchronized (sLock) { if (sCachedUuid != null) { return sCachedUuid; } File file = new File(UUID_FILE_PATH); if (file.exists()) { try (BufferedReader reader = new BufferedReader(new FileReader(file))) { String line = reader.readLine(); if (isValidUuid(line)) { sCachedUuid = line.trim(); Log.i(TAG, "Loaded UUID from " + UUID_FILE_PATH); return sCachedUuid; } else { Log.w(TAG, "Invalid UUID format, regenerating..."); } } catch (IOException e) { Log.e(TAG, "Failed to read UUID file", e); } } // 生成新 UUID String newUuid = UUID.randomUUID().toString(); sCachedUuid = newUuid; // 写入文件 File parentDir = file.getParentFile(); if (parentDir != null && !parentDir.exists()) { parentDir.mkdirs(); } try (FileWriter writer = new FileWriter(file)) { writer.write(newUuid); writer.flush(); Runtime.getRuntime().exec("chmod 644 " + UUID_FILE_PATH).waitFor(); Log.i(TAG, "Saved new UUID: " + newUuid); } catch (IOException | InterruptedException e) { Log.e(TAG, "Failed to write UUID", e); } } return sCachedUuid; } private static boolean isValidUuid(String str) { if (str == null || str.length() != 36) return false; return str.matches( "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-5][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$" ); } } registerService(Context.UUID_SERVICE, UuidManager.class, new CachedServiceFetcher<UuidManager>() { @Override public UuidManager createService(ContextImpl ctx) { return UuidManager.create(); //通过公共静态方法创建 } }); try { Slog.i(TAG, "Starting UUID Service"); UuidService uuidService = new UuidService(mSystemContext); ServiceManager.addService(Context.UUID_SERVICE, uuidService); } catch (Throwable e) { reportWtf("starting UUID Service", e); } <!-- caiwenlu add --> <permission android:name="android.permission.READ_DEVICE_UUID" android:protectionLevel="signature|privileged" android:label="@string/permlab_readDeviceUuid" android:description="@string/permdesc_readDeviceUuid" /> <!-- caiwenlu add end --> //caiwenlu add /** * Use with {@link #getSystemService(Class)} to retrieve a {@link UuidManager}. */ public static final String UUID_SERVICE = "uuid"; //caiwenlu add end UuidManager uuidManager = getSystemService(UuidManager.class); if (uuidManager != null) { String uuid = uuidManager.getOrCreatePersistentUuid(); Log.d("UUID", "Device UUID: " + uuid); } else { Log.e("UUID", "UuidManager is null - check if service started"); }
最新发布
11-25
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值