Android灯光系统(硬件访问服务框架)

Java类:LightsService.java

LightsService.java通过调用,LightsService JNI来实现com.android.server包中的LightsService类。

这个类不是平台API,被Android系统JAVA框架中的其他一些部分调用。

/* * Copyright (C) 2008 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 com.android.server.lights;import android.app.ActivityManager;import android.content.Context;import android.os.Handler;import android.os.IBinder;import android.os.Message;import android.os.PowerManager;import android.os.Trace;import android.provider.Settings;import android.util.Slog;import android.view.SurfaceControl;import com.android.server.SystemService;public class LightsService extends SystemService {         static final String TAG = "LightsService";    static final boolean DEBUG = false;    final LightImpl mLights[] = new LightImpl[LightsManager.LIGHT_ID_COUNT];    private final class LightImpl extends Light {             private final IBinder mDisplayToken;        private final int mSurfaceControlMaximumBrightness;        private LightImpl(Context context, int id) {                 mId = id;            mDisplayToken = SurfaceControl.getInternalDisplayToken();            final boolean brightnessSupport = SurfaceControl.getDisplayBrightnessSupport(                    mDisplayToken);            if (DEBUG) {                     Slog.d(TAG, "Display brightness support: " + brightnessSupport);            }            int maximumBrightness = 0;            if (brightnessSupport) {                     PowerManager pm = context.getSystemService(PowerManager.class);                if (pm != null) {                         maximumBrightness = pm.getMaximumScreenBrightnessSetting();                }            }            mSurfaceControlMaximumBrightness = maximumBrightness;        }        @Override        public void setBrightness(int brightness) {                 setBrightness(brightness, BRIGHTNESS_MODE_USER);        }        @Override        public void setBrightness(int brightness, int brightnessMode) {                 synchronized (this) {                     // LOW_PERSISTENCE cannot be manually set                if (brightnessMode == BRIGHTNESS_MODE_LOW_PERSISTENCE) {                         Slog.w(TAG, "setBrightness with LOW_PERSISTENCE unexpected #" + mId +                            ": brightness=0x" + Integer.toHexString(brightness));                    return;                }                // Ideally, we'd like to set the brightness mode through the SF/HWC as well, but                // right now we just fall back to the old path through Lights brightessMode is                // anything but USER or the device shouldBeInLowPersistenceMode().                if (brightnessMode == BRIGHTNESS_MODE_USER && !shouldBeInLowPersistenceMode()                        && mSurfaceControlMaximumBrightness == 255) {                         // TODO: the last check should be mSurfaceControlMaximumBrightness != 0; the                    // reason we enforce 255 right now is to stay consistent with the old path. In                    // the future, the framework should be refactored so that brightness is a float                    // between 0.0f and 1.0f, and the actual number of supported brightness levels                    // is determined in the device-specific implementation.                    if (DEBUG) {                             Slog.d(TAG, "Using new setBrightness path!");                    }                    SurfaceControl.setDisplayBrightness(mDisplayToken,                            (float) brightness / mSurfaceControlMaximumBrightness);                } else {                         int color = brightness & 0x000000ff;                    color = 0xff000000 | (color << 16) | (color << 8) | color;                    setLightLocked(color, LIGHT_FLASH_NONE, 0, 0, brightnessMode);                }            }        }        @Override        public void setColor(int color) {                 synchronized (this) {                     setLightLocked(color, LIGHT_FLASH_NONE, 0, 0, 0);            }        }        @Override        public void setFlashing(int color, int mode, int onMS, int offMS) {                 synchronized (this) {                     setLightLocked(color, mode, onMS, offMS, BRIGHTNESS_MODE_USER);            }        }        @Override        public void pulse() {                 pulse(0x00ffffff, 7);        }        @Override        public void pulse(int color, int onMS) {                 synchronized (this) {                     if (mColor == 0 && !mFlashing) {                         setLightLocked(color, LIGHT_FLASH_HARDWARE, onMS, 1000,                            BRIGHTNESS_MODE_USER);                    mColor = 0;                    mH.sendMessageDelayed(Message.obtain(mH, 1, this), onMS);                }            }        }        @Override        public void turnOff() {                 synchronized (this) {                     setLightLocked(0, LIGHT_FLASH_NONE, 0, 0, 0);            }        }        @Override        public void setVrMode(boolean enabled) {                 synchronized (this) {                     if (mVrModeEnabled != enabled) {                         mVrModeEnabled = enabled;                    mUseLowPersistenceForVR =                            (getVrDisplayMode() == Settings.Secure.VR_DISPLAY_MODE_LOW_PERSISTENCE);                    if (shouldBeInLowPersistenceMode()) {                             mLastBrightnessMode = mBrightnessMode;                    }                    // NOTE: We do not trigger a call to setLightLocked here.  We do not know the                    // current brightness or other values when leaving VR so we avoid any incorrect                    // jumps. The code that calls this method will immediately issue a brightness                    // update which is when the change will occur.                }            }        }        private void stopFlashing() {                 synchronized (this) {                     setLightLocked(mColor, LIGHT_FLASH_NONE, 0, 0, BRIGHTNESS_MODE_USER);            }        }        private void setLightLocked(int color, int mode, int onMS, int offMS, int brightnessMode) {                 if (shouldBeInLowPersistenceMode()) {                     brightnessMode = BRIGHTNESS_MODE_LOW_PERSISTENCE;            } else if (brightnessMode == BRIGHTNESS_MODE_LOW_PERSISTENCE) {                     brightnessMode = mLastBrightnessMode;            }            if (!mInitialized || color != mColor || mode != mMode || onMS != mOnMS ||                    offMS != mOffMS || mBrightnessMode != brightnessMode) {                     if (DEBUG) Slog.v(TAG, "setLight #" + mId + ": color=#"                        + Integer.toHexString(color) + ": brightnessMode=" + brightnessMode);                mInitialized = true;                mLastColor = mColor;                mColor = color;                mMode = mode;                mOnMS = onMS;                mOffMS = offMS;                mBrightnessMode = brightnessMode;                Trace.traceBegin(Trace.TRACE_TAG_POWER, "setLight(" + mId + ", 0x"                        + Integer.toHexString(color) + ")");                try {                         setLight_native(mId, color, mode, onMS, offMS, brightnessMode);                } finally {                         Trace.traceEnd(Trace.TRACE_TAG_POWER);                }            }        }        private boolean shouldBeInLowPersistenceMode() {                 return mVrModeEnabled && mUseLowPersistenceForVR;        }        private int mId;        private int mColor;        private int mMode;        private int mOnMS;        private int mOffMS;        private boolean mFlashing;        private int mBrightnessMode;        private int mLastBrightnessMode;        private int mLastColor;        private boolean mVrModeEnabled;        private boolean mUseLowPersistenceForVR;        private boolean mInitialized;    }    public LightsService(Context context) {             super(context);        for (int i = 0; i < LightsManager.LIGHT_ID_COUNT; i++) {                 mLights[i] = new LightImpl(context, i);        }    }    @Override    public void onStart() {             publishLocalService(LightsManager.class, mService);    }    @Override    public void onBootPhase(int phase) {         }    private int getVrDisplayMode() {             int currentUser = ActivityManager.getCurrentUser();        return Settings.Secure.getIntForUser(getContext().getContentResolver(),                Settings.Secure.VR_DISPLAY_MODE,                /*default*/Settings.Secure.VR_DISPLAY_MODE_LOW_PERSISTENCE,                currentUser);    }    private final LightsManager mService = new LightsManager() {             @Override        public Light getLight(int id) {                 if (0 <= id && id < LIGHT_ID_COUNT) {                     return mLights[id];            } else {                     return null;            }        }    };    private Handler mH = new Handler() {             @Override        public void handleMessage(Message msg) {                 LightImpl light = (LightImpl)msg.obj;            light.stopFlashing();        }    };    static native void setLight_native(int light, int color, int mode,            int onMS, int offMS, int brightnessMode);}

本地类:com_android_server_lights_LightsService.cpp

这个类调用硬件抽象层,也同时提供了JNI的接口。

/* * Copyright (C) 2009 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. */#define LOG_TAG "LightsService"#include "jni.h"#include #include "android_runtime/AndroidRuntime.h"#include #include #include #include #include #include #include namespace android {     using Brightness = ::android::hardware::light::V2_0::Brightness;using Flash      = ::android::hardware::light::V2_0::Flash;using ILight     = ::android::hardware::light::V2_0::ILight;using LightState = ::android::hardware::light::V2_0::LightState;using Status     = ::android::hardware::light::V2_0::Status;using Type       = ::android::hardware::light::V2_0::Type;template<typename T>using Return     = ::android::hardware::Return<T>;static bool sLightSupported = true;static bool validate(jint light, jint flash, jint brightness) {         bool valid = true;    if (light < 0 || light >= static_cast<jint>(Type::COUNT)) {             ALOGE("Invalid light parameter %d.", light);        valid = false;    }    if (flash != static_cast<jint>(Flash::NONE) &&        flash != static_cast<jint>(Flash::TIMED) &&        flash != static_cast<jint>(Flash::HARDWARE)) {             ALOGE("Invalid flash parameter %d.", flash);        valid = false;    }    if (brightness != static_cast<jint>(Brightness::USER) &&        brightness != static_cast<jint>(Brightness::SENSOR) &&        brightness != static_cast<jint>(Brightness::LOW_PERSISTENCE)) {             ALOGE("Invalid brightness parameter %d.", brightness);        valid = false;    }    if (brightness == static_cast<jint>(Brightness::LOW_PERSISTENCE) &&        light != static_cast<jint>(Type::BACKLIGHT)) {             ALOGE("Cannot set low-persistence mode for non-backlight device.");        valid = false;    }    return valid;}static LightState constructState(        jint colorARGB,        jint flashMode,        jint onMS,        jint offMS,        jint brightnessMode){         Flash flash = static_cast<Flash>(flashMode);    Brightness brightness = static_cast<Brightness>(brightnessMode);    LightState state{     };    if (brightness == Brightness::LOW_PERSISTENCE) {             state.flashMode = Flash::NONE;    } else {             // Only set non-brightness settings when not in low-persistence mode        state.flashMode = flash;        state.flashOnMs = onMS;        state.flashOffMs = offMS;    }    state.color = colorARGB;    state.brightnessMode = brightness;    return state;}static void processReturn(        const Return<Status> &ret,        Type type,        const LightState &state) {         if (!ret.isOk()) {             ALOGE("Failed to issue set light command.");        return;    }    switch (static_cast<Status>(ret)) {             case Status::SUCCESS:            break;        case Status::LIGHT_NOT_SUPPORTED:            ALOGE("Light requested not available on this device. %d", type);            break;        case Status::BRIGHTNESS_NOT_SUPPORTED:            ALOGE("Brightness parameter not supported on this device: %d",                state.brightnessMode);            break;        case Status::UNKNOWN:        default:            ALOGE("Unknown error setting light.");    }}static void setLight_native(        JNIEnv* /* env */,        jobject /* clazz */,        jint light,        jint colorARGB,        jint flashMode,        jint onMS,        jint offMS,        jint brightnessMode) {         if (!sLightSupported) {             return;    }    if (!validate(light, flashMode, brightnessMode)) {             return;    }    Type type = static_cast<Type>(light);    LightState state = constructState(        colorARGB, flashMode, onMS, offMS, brightnessMode);    {             android::base::Timer t;        sp<ILight> hal = ILight::getService();        if (hal == nullptr) {                 sLightSupported = false;            return;        }        Return<Status> ret = hal->setLight(type, state);        processReturn(ret, type, state);        if (t.duration() > 50ms) ALOGD("Excessive delay setting light");    }}static const JNINativeMethod method_table[] = {         {      "setLight_native", "(IIIIII)V", (void*)setLight_native },};int register_android_server_LightsService(JNIEnv *env) {         return jniRegisterNativeMethods(env, "com/android/server/lights/LightsService",            method_table, NELEM(method_table));}};

硬件抽象层:light.c

/* * Copyright (C) 2008 The Android Open Source Project * Copyright (C) 2014 The  Linux Foundation. All rights reserved. * * 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. */#include #include #include #include #include #include #include #include #include #include #include #include #include /* * Change this to 1 to support battery notifications via BatteryService */#define LIGHTS_SUPPORT_BATTERY 0#define CG_COLOR_ID_PROPERTY "ro.boot.hardware.color"static pthread_once_t g_init = PTHREAD_ONCE_INIT;static pthread_mutex_t g_lock = PTHREAD_MUTEX_INITIALIZER;static struct light_state_t g_notification;static struct light_state_t g_battery;static int g_last_backlight_mode = BRIGHTNESS_MODE_USER;static int g_attention = 0;static int rgb_brightness_ratio = 255;char const*const RED_LED_FILE        = "/sys/class/leds/red/brightness";char const*const GREEN_LED_FILE        = "/sys/class/leds/green/brightness";char const*const BLUE_LED_FILE        = "/sys/class/leds/blue/brightness";char const*const LCD_FILE        = "/sys/class/leds/lcd-backlight/brightness";char const*const PERSISTENCE_FILE        = "/sys/class/leds/lcd-backlight/low_persistence";char const*const RED_BLINK_FILE        = "/sys/class/leds/red/blink";char const*const GREEN_BLINK_FILE        = "/sys/class/leds/green/blink";char const*const BLUE_BLINK_FILE        = "/sys/class/leds/blue/blink";char const*const RED_ON_OFF_MS_FILE        = "/sys/class/leds/red/on_off_ms";char const*const GREEN_ON_OFF_MS_FILE        = "/sys/class/leds/green/on_off_ms";char const*const BLUE_ON_OFF_MS_FILE        = "/sys/class/leds/blue/on_off_ms";char const*const RED_RGB_START_FILE        = "/sys/class/leds/red/rgb_start";char const*const GREEN_RGB_START_FILE        = "/sys/class/leds/green/rgb_start";char const*const BLUE_RGB_START_FILE        = "/sys/class/leds/blue/rgb_start";/** * device methods */void init_globals(void){         char color_id_prop[PROPERTY_VALUE_MAX] = {     ""};    // init the mutex    pthread_mutex_init(&g_lock, NULL);    // check CG color    property_get(CG_COLOR_ID_PROPERTY, color_id_prop, "DEF00");    if (strcmp(color_id_prop, "GRA00") == 0) {             rgb_brightness_ratio = 25;    } else if (strcmp(color_id_prop, "SLV00") == 0) {             rgb_brightness_ratio = 15;    } else if (strcmp(color_id_prop, "BLU00") == 0) {             rgb_brightness_ratio = 15;    } else {             rgb_brightness_ratio = 20;    }}static intwrite_int(char const* path, int value){         int fd;    static int already_warned = 0;    fd = open(path, O_WRONLY);    if (fd >= 0) {             char buffer[20];        size_t bytes = snprintf(buffer, sizeof(buffer), "%d\n", value);        if(bytes >= sizeof(buffer)) return -EINVAL;        ssize_t amt = write(fd, buffer, bytes);        close(fd);        return amt == -1 ? -errno : 0;    } else {             if (already_warned == 0) {                 ALOGE("write_int failed to open %s\n", path);            already_warned = 1;        }        return -errno;    }}static intwrite_double_int(char const* path, int value1, int value2){         int fd;    static int already_warned = 0;    fd = open(path, O_WRONLY);    if (fd >= 0) {             char buffer[20];        size_t bytes = snprintf(buffer, sizeof(buffer), "%d %d\n", value1, value2);        if(bytes >= sizeof(buffer)) return -EINVAL;        ssize_t amt = write(fd, buffer, bytes);        close(fd);        return amt == -1 ? -errno : 0;    } else {             if (already_warned == 0) {                 ALOGE("write_int failed to open %s\n", path);            already_warned = 1;        }        return -errno;    }}static intis_lit(struct light_state_t const* state){         return state->color & 0x00ffffff;}static intrgb_to_brightness(struct light_state_t const* state){         int color = state->color & 0x00ffffff;    return ((77*((color>>16)&0x00ff))            + (150*((color>>8)&0x00ff)) + (29*(color&0x00ff))) >> 8;}static intset_light_backlight(struct light_device_t* dev,        struct light_state_t const* state){         int err = 0;    int brightness = rgb_to_brightness(state);    unsigned int lpEnabled = state->brightnessMode == BRIGHTNESS_MODE_LOW_PERSISTENCE;    if(!dev) {             return -1;    }    pthread_mutex_lock(&g_lock);    // If we're not in lp mode and it has been enabled or if we are in lp mode    // and it has been disabled send an ioctl to the display with the update    if ((g_last_backlight_mode != state->brightnessMode && lpEnabled) ||            (!lpEnabled && g_last_backlight_mode == BRIGHTNESS_MODE_LOW_PERSISTENCE)) {             if ((err = write_int(PERSISTENCE_FILE, lpEnabled)) != 0) {                 ALOGE("%s: Failed to write to %s: %s\n", __FUNCTION__, PERSISTENCE_FILE,                    strerror(errno));        }    }    g_last_backlight_mode = state->brightnessMode;    if (!err) {             err = write_int(LCD_FILE, brightness);    }    pthread_mutex_unlock(&g_lock);    return err;}static intset_speaker_light_locked(struct light_device_t* dev,        struct light_state_t const* state){         int red, green, blue;    int blink;    int onMS, offMS;    unsigned int colorRGB;    if(!dev) {             return -1;    }    switch (state->flashMode) {             case LIGHT_FLASH_TIMED:            onMS = state->flashOnMS;            offMS = state->flashOffMS;            break;        case LIGHT_FLASH_NONE:        default:            onMS = 0;            offMS = 0;            break;    }    colorRGB = state->color;#if 0    ALOGD("set_speaker_light_locked mode %d, colorRGB=%08X, onMS=%d, offMS=%d\n",            state->flashMode, colorRGB, onMS, offMS);#endif    red = ((colorRGB >> 16) & 0xFF) * rgb_brightness_ratio / 255;    green = ((colorRGB >> 8) & 0xFF) * rgb_brightness_ratio / 255;    blue = (colorRGB & 0xFF) * rgb_brightness_ratio / 255;    write_double_int(RED_ON_OFF_MS_FILE, onMS, offMS);    write_int(RED_LED_FILE, red);    write_double_int(GREEN_ON_OFF_MS_FILE, onMS, offMS);    write_int(GREEN_LED_FILE, green);    write_double_int(BLUE_ON_OFF_MS_FILE, onMS, offMS);    write_int(BLUE_LED_FILE, blue);    if(!write_int(RED_RGB_START_FILE, 1))        if(!write_int(GREEN_RGB_START_FILE, 1))            if(!write_int(BLUE_RGB_START_FILE, 1))                return -1;    return 0;}static voidhandle_speaker_battery_locked(struct light_device_t* dev){         if (is_lit(&g_battery)) {             set_speaker_light_locked(dev, &g_battery);    } else {             set_speaker_light_locked(dev, &g_notification);    }}#if LIGHTS_SUPPORT_BATTERYstatic intset_light_battery(struct light_device_t* dev,        struct light_state_t const* state){         pthread_mutex_lock(&g_lock);    g_battery = *state;    handle_speaker_battery_locked(dev);    pthread_mutex_unlock(&g_lock);    return 0;}#endifstatic intset_light_notifications(struct light_device_t* dev,        struct light_state_t const* state){         pthread_mutex_lock(&g_lock);    g_notification = *state;    handle_speaker_battery_locked(dev);    pthread_mutex_unlock(&g_lock);    return 0;}static intset_light_attention(struct light_device_t* dev,        struct light_state_t const* state){         pthread_mutex_lock(&g_lock);    if (state->flashMode == LIGHT_FLASH_HARDWARE) {             g_attention = state->flashOnMS;    } else if (state->flashMode == LIGHT_FLASH_NONE) {             g_attention = 0;    }    handle_speaker_battery_locked(dev);    pthread_mutex_unlock(&g_lock);    return 0;}/** Close the lights device */static intclose_lights(struct light_device_t *dev){         if (dev) {             free(dev);    }    return 0;}/******************************************************************************//** * module methods *//** Open a new instance of a lights device using name */static int open_lights(const struct hw_module_t* module, char const* name,        struct hw_device_t** device){         int (*set_light)(struct light_device_t* dev,            struct light_state_t const* state);    if (0 == strcmp(LIGHT_ID_BACKLIGHT, name))        set_light = set_light_backlight;#if LIGHTS_SUPPORT_BATTERY    else if (0 == strcmp(LIGHT_ID_BATTERY, name))        set_light = set_light_battery;#endif    else if (0 == strcmp(LIGHT_ID_NOTIFICATIONS, name))        set_light = set_light_notifications;    else if (0 == strcmp(LIGHT_ID_ATTENTION, name))        set_light = set_light_attention;    else        return -EINVAL;    pthread_once(&g_init, init_globals);    struct light_device_t *dev = malloc(sizeof(struct light_device_t));    if(!dev)        return -ENOMEM;    memset(dev, 0, sizeof(*dev));    dev->common.tag = HARDWARE_DEVICE_TAG;    dev->common.version = LIGHTS_DEVICE_API_VERSION_2_0;    dev->common.module = (struct hw_module_t*)module;    dev->common.close = (int (*)(struct hw_device_t*))close_lights;    dev->set_light = set_light;    *device = (struct hw_device_t*)dev;    return 0;}static struct hw_module_methods_t lights_module_methods = {         .open =  open_lights,};/* * The lights Module */struct hw_module_t HAL_MODULE_INFO_SYM = {         .tag = HARDWARE_MODULE_TAG,    .version_major = 1,    .version_minor = 0,    .id = LIGHTS_HARDWARE_MODULE_ID,    .name = "lights Module",    .author = "Google, Inc.",    .methods = &lights_module_methods,};

驱动层:led_driver.c

#include #include #include #include #include #include                                                                                            #include #define PAD_LEDCON 0x01c208b4#define PAD_LEDDAT 0x01c208c4unsigned int *pad_con = NULL;unsigned int *pad_dat = NULL;struct led_classdev * cdev;void leds_brightness_set(struct led_classdev *led_cdev,  enum led_brightness brightness){     if(brightness){     writel((readl(pad_dat)&(~(1<<2))),pad_dat);}else{     writel((readl(pad_dat)|(1<<2)),pad_dat);}}static int __init leds_init(void){     int ret;//1.分配结构体cdev = kzalloc(sizeof(*cdev),GFP_KERNEL);if(cdev == NULL){     printk("alloc led_classdev is fail.\n");return -ENOMEM;}//2.结构体的初始化cdev->name = "myleds";cdev->max_brightness = LED_FULL;cdev->brightness_set = leds_brightness_set;cdev->flags |= LED_CORE_SUSPENDRESUME;//3.硬件相关的操作pad_con = (unsigned int *)ioremap(PAD_LEDCON,4);pad_dat = (unsigned int *)ioremap(PAD_LEDDAT,4);writel(((readl(pad_con)&(~(0xf<<8)))|(1<<8)),pad_con);writel((readl(pad_dat)|(1<<2)),pad_dat);//4.注册ret = led_classdev_register(NULL,cdev);if(ret < 0){     printk("register led_classdev is fail.\n");return -EAGAIN;}return 0;}static void __exit leds_exit(void){     //注销led_classdev_unregister(cdev);}module_init(leds_init);module_exit(leds_exit);MODULE_LICENSE("GPL");

更多相关文章

  1. Android Retrofit 2.0框架上传图片,视频解决方案
  2. Android核心分析(21)----Android应用框架之AndroidApplication
  3. 细数Android Studio中使用junit4测试框架中的坑
  4. Android开发——Android搜索框架(二)
  5. Android热更新框架Tinker无法更新?
  6. Android N 指纹框架
  7. android 目录结构,adb环境变量配置
  8. [置顶] android orm映射框架(类似hibernate)基本使用

随机推荐

  1. Android(安卓)ADB实现解析
  2. SparseArray详解,我说SparseArray,你说要!
  3. Android中绑定SQLite到ListActivity
  4. window.navigator.userAgent的用处
  5. android 百度推送的集成
  6. Android(安卓)EditText限制输入数字和字
  7. Android基于AudioManager、PhoneStateLis
  8. Android(安卓)Activity分析
  9. android TextInputLayout
  10. 【Android(安卓)Developers Training】 1