关于安卓的定制、移植,网上相关的教程已经很多了,我收集了一些适用于我自己项目的,发到博客上记录一下。
全志官方是提供了 A64 Android_M快速移植指南.pdf,但是对这个项目来说还不够全,所以增加了些内容。

devices-hero_620px.webp

资料的话可以查看:

全志 Allwinner A64 硬件开发资料 参考PCB设计 SDK开发文档 – 吴川斌的博客 (mr-wu.cn)

全志A20 / A64 / A133 / D1 / T113-I/V83x /V85x Tina / Android 文档合集/集合,很全! / 全志 SOC / WhyCan Forum(哇酷开发者社区)

PINE A64 - PINE64

编译阶段

1.修改WIFI

workspace/a64/android/out/target/product/设备名/system/etc/wifi

workspace/a64/android/external/wpa_supplicant_8/wpa_supplicant

update_config=1
eapol_version=1
ap_scan=1
fast_reauth=1
pmf=1
ctrl_interface=/var/run/wpa_supplicant
update_config=1

network={
  ssid="WIFI名字"
  psk="密码"
  key_mgmt=WPA-PSK
}

2.关闭双击两次电源键打开相机

<!-- Allow the gesture to double tap the power button twice to start the camera while the device
     is non-interactive. -->
<bool name="config_cameraDoubleTapPowerGestureEnabled">false</bool>

4.关闭电源键短按事件和长按时弹出交互框

如果不需要短按电源键休眠屏幕和长按电源键弹出关机重启交互框,那也可以将其禁用。

workspace/a64/android/frameworks/base/services/core/java/com/android/server/policy

PhoneWindowManager.java

    private void powerPress(long eventTime, boolean interactive, int count) {
        if (mScreenOnEarly && !mScreenOnFully) {
            Slog.i(TAG, "Suppressed redundant power key press while "
                    + "already in the process of turning the screen on.");
            return;
        }
        //恒等于2,短按不休眠屏幕
        count = 2;
        if (count == 2) {
            powerMultiPressAction(eventTime, interactive, mDoublePressOnPowerBehavior);
        } else if (count == 3) {
            powerMultiPressAction(eventTime, interactive, mTriplePressOnPowerBehavior);
        } else if (interactive && !mBeganFromNonInteractive) {
            switch (mShortPressOnPowerBehavior) {
                case SHORT_PRESS_POWER_NOTHING:
                    break;
                case SHORT_PRESS_POWER_GO_TO_SLEEP:
                    mPowerManager.goToSleep(eventTime,
                            PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON, 0);
                    break;
                case SHORT_PRESS_POWER_REALLY_GO_TO_SLEEP:
                    mPowerManager.goToSleep(eventTime,
                            PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON,
                            PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE);
                    break;
                case SHORT_PRESS_POWER_REALLY_GO_TO_SLEEP_AND_GO_HOME:
                    mPowerManager.goToSleep(eventTime,
                            PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON,
                            PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE);
                    launchHomeFromHotKey();
                    break;
                case SHORT_PRESS_POWER_GO_HOME:
                    launchHomeFromHotKey(true /* awakenFromDreams */, false /*respectKeyguard*/);
                    break;
            }
        }
    }
    private void powerLongPress() {
        final int behavior = getResolvedLongPressOnPowerBehavior();
        switch (behavior) {
        case LONG_PRESS_POWER_NOTHING:
            break;
        case LONG_PRESS_POWER_GLOBAL_ACTIONS:
            mPowerKeyHandled = true;
            if (!performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false)) {
                performAuditoryFeedbackForAccessibilityIfNeed();
            }
            //弹出选择关机还是重启的对话框
            // showGlobalActionsInternal();
            break;
        case LONG_PRESS_POWER_SHUT_OFF:
        case LONG_PRESS_POWER_SHUT_OFF_NO_CONFIRM:
            mPowerKeyHandled = true;
            performHapticFeedbackLw(null, HapticFeedbackConstants.LONG_PRESS, false);
            sendCloseSystemWindows(SYSTEM_DIALOG_REASON_GLOBAL_ACTIONS);
            mWindowManagerFuncs.shutdown(behavior == LONG_PRESS_POWER_SHUT_OFF);
            break;
        }
    }

5.修改欲打包的程序

workspace/a64/android/device/softwinner/设备名

设备名.mk

#PRODUCT_PACKAGES += Launcher3

PRODUCT_PACKAGES += \
#    ESFileExplorer \
#    VideoPlayer \
    Bluetooth \
    sendPhotos
#   PartnerChromeCustomizationsProvider

去掉不需要的程序。

6.修改产品设备名

PRODUCT_BRAND := Allwinner
PRODUCT_NAME := cyqsda64_v1
PRODUCT_DEVICE := cyqsda64-v1
PRODUCT_MODEL := CyqsdA64 v1
PRODUCT_MANUFACTURER := cyqsd

8.去除原有启动器启动权限

我使用的安卓源码是自带有启动器com.android.launcher3.Launcher的,如果定制开发的程序是启动器类型的,那么旧的启动器就需要去除,当然可以直接删除改启动的源码,我使用的办法比较偷懒,是去除intent。

        <activity
            android:name="com.android.launcher3.Launcher"
            android:launchMode="singleTask"
            android:clearTaskOnLaunch="true"
            android:stateNotNeeded="true"
            android:theme="@style/Theme"
            android:windowSoftInputMode="adjustPan"
            android:screenOrientation="nosensor"
            android:resumeWhilePausing="true"
            android:taskAffinity=""
            android:enabled="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.HOME" />
                <category android:name="android.intent.category.DEFAULT" />
                <category android:name="android.intent.category.MONKEY"/>
            </intent-filter>
        </activity>

9.开机关闭USB调试

workspace\a64\android\packages\apps\Provision\src\com\android\provision\DefaultActivity.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.provision;

import android.app.Activity;
import android.content.ComponentName;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.provider.Settings;

/**
 * Application that sets the provisioned bit, like SetupWizard does.
 */
public class DefaultActivity extends Activity {

    @Override
    protected void onCreate(Bundle icicle) {
        super.onCreate(icicle);

        // Add a persistent setting to allow other apps to know the device has been provisioned.
        Settings.Global.putInt(getContentResolver(), Settings.Global.DEVICE_PROVISIONED, 1);
        Settings.Secure.putInt(getContentResolver(), Settings.Secure.USER_SETUP_COMPLETE, 1);

        // remove this activity from the package manager.
        PackageManager pm = getPackageManager();
        ComponentName name = new ComponentName(this, DefaultActivity.class);
        pm.setComponentEnabledSetting(name, PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
                PackageManager.DONT_KILL_APP);

        // terminate the activity.
        finish();
    }
}

加入:

        if (!android.os.SystemProperties.getBoolean("ro.inet.adb_enabled",true)) {
           Settings.Secure.putInt(getContentResolver(), Settings.Secure.ADB_ENABLED, 0);
       }

10.WIFI感叹号自动连接

自从Android5.0开始就有了WIFI未联网时会有感叹号,但是因为项目的原因,设备会处于没有互联网的状态,所以感叹号我们得去掉。

目录位于:workspace/a64/android/frameworks/opt/net/wifi/service/java/com/android/server/wifi

WifiAutoJoinController.java

            // NOTE: If this condition is updated, update NETWORK_STATUS_UNWANTED_DISABLE_AUTOJOIN.
            if (config.numNoInternetAccessReports > 0
                    && !isLastSelected
                    && !config.validatedInternetAccess) {
                // Avoid autoJoining this network because last time we used it, it didn't
                // have internet access, and we never manage to validate internet access on this
                // network configuration
                if (DBG) {
                    logDbg("attemptAutoJoin skip candidate due to no InternetAccess  "
                            + config.configKey(true)
                            + " num reports " + config.numNoInternetAccessReports);
                }
                //continue;

workspace/a64/android/frameworks/base/services/core/java/com/android/server/connectivity

NetworkMonitor.java

    public NetworkMonitor(Context context, Handler handler, NetworkAgentInfo networkAgentInfo,
            NetworkRequest defaultRequest) {
        // Add suffix indicating which NetworkMonitor we're talking about.
        super(TAG + networkAgentInfo.name());

        mContext = context;
        mConnectivityServiceHandler = handler;
        mNetworkAgentInfo = networkAgentInfo;
        mTelephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
        mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
        mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
        mDefaultRequest = defaultRequest;

        addState(mDefaultState);
        addState(mValidatedState, mDefaultState);
        addState(mMaybeNotifyState, mDefaultState);
            addState(mEvaluatingState, mMaybeNotifyState);
            addState(mCaptivePortalState, mMaybeNotifyState);
        addState(mLingeringState, mDefaultState);
        setInitialState(mDefaultState);

        mServer = Settings.Global.getString(mContext.getContentResolver(),
                Settings.Global.CAPTIVE_PORTAL_SERVER);
        if (mServer == null) mServer = DEFAULT_SERVER;

        mLingerDelayMs = SystemProperties.getInt(LINGER_DELAY_PROPERTY, DEFAULT_LINGER_DELAY_MS);

        //mIsCaptivePortalCheckEnabled = Settings.Global.getInt(mContext.getContentResolver(),
        //        Settings.Global.CAPTIVE_PORTAL_DETECTION_ENABLED, 1) == 1;
        mIsCaptivePortalCheckEnabled = false;
        start();
    }

11.隐藏状态栏

状态栏也需要隐藏掉。

workspace/a64/android/frameworks/base/core/res/res/values

dimens.xml

    <integer name="max_action_buttons">2</integer>
    <dimen name="toast_y_offset">64dip</dimen>
    <!-- Height of the status bar -->
    <dimen name="status_bar_height">24dp</dimen>
    <!-- Height of the bottom navigation / system bar. -->
    <dimen name="navigation_bar_height">48dp</dimen>

修改status_bar_height的值即可。

12.关闭低电量时提醒

workspace/a64/android/frameworks/base/packages/SystemUI/src/com/android/systemui/power

                if (!plugged
                        && (bucket < oldBucket || oldPlugged)
                        && mBatteryStatus != BatteryManager.BATTERY_STATUS_UNKNOWN
                        && bucket < 0) {
                    // only play SFX when the dialog comes up or the bucket changes
                    final boolean playSound = bucket != oldBucket || oldPlugged;
                    mWarnings.showLowBatteryWarning(playSound);
                } else if (plugged || (bucket > oldBucket && bucket > 0)) {
                    mWarnings.dismissLowBatteryWarning();
                } else {
                    mWarnings.updateLowBatteryWarning();
                }

禁用掉 mWarnings.showLowBatteryWarning(playSound);

打包修改

下面是打包阶段需要做的修改,因为打包脚本可能因为定制的厂商位置不一样,所以就不写文件路径了,以实际使用为准。

1.去除菜单栏、状态栏

使用qemu.hw.mainkeys来隐藏虚拟按键,这样就可以引导用户使用我们设计的独立按键来完成界面交互。

qemu.hw.mainkeys=1
ro.inet.adb_enabled=false
persist.sys.usb.config=charging

2.修改按钮映射

我的设备是有按键映射到系统的,所以修改一下映射按键的定义。

workspace/a64/android/device/softwinner/设备名/configs

key 115      VOLUME_UP
key 114      VOLUME_DOWN

修改为:

key 115      BACK

3.停用USB

不希望使用USB的话,还需要在配置文件中禁用USB。把usbcX_used改为0即可。

;--------------------------------
;[usbc0]: usbc0 configuration.
;usb_used: usb controller enable. 0-disable, 1-enable.
;usb_port_type: usb mode. 0-device, 1-host, 2-otg.
;usb_detect_type: usb hotplug detect mode. 0-none, 1-vbus/id detect, 2-id/dpdm detect.
;usb_detect_mode: usb otg switch has two config. 0-thread scan, 1-id gpio interrupt.
;usb_id_gpio: usb id detect IO.
;usb_det_vbus_gpio: USB DET_VBUS has two config. (1)gpio pin; (2)"axp_ctrl", use axp intf.
;usb_drv_vbus_gpio: USB DRY_VBUS has two config. (1)gpio pin; (2)"axp_ctrl", use axp intf.
;--------------------------------
;--------------------------------
;---       USB0 CONFIG
;--------------------------------
[usbc0]
usbc0_used          = 0
usb_port_type       = 2
usb_detect_type     = 1
usb_detect_mode     = 1
usb_id_gpio         = port:PH09<0><1><default><default>
;usb_det_vbus_gpio   = "axp_ctrl"
usb_det_vbus_gpio   = "gpio"
usb_drv_vbus_gpio   = port:power3<1><0><default><0>
usb_host_init_state = 0
usb_regulator_io    = "nocare"
usb_wakeup_suspend  = 0
;---       USB Device
usb_luns            = 3
usb_serial_unique   = 1
usb_serial_number   = "20080411"
rndis_wceis         = 1

;--------------------------------
;---       USB1 CONFIG
;--------------------------------
[usbc1]
usbc1_used          = 0
usb_drv_vbus_gpio   = port:PB06<1><0><default><0>
usb_host_init_state = 1
usb_regulator_io    = "nocare"
usb_wakeup_suspend  = 0
;---  HSIC config
usb_hsic_used          = 0
usb_hsic_regulator_io  = "vcc-hsic-12"
;---  Marvell 4G HSIC
usb_hsic_ctrl          = 0
usb_hsic_rdy_gpio      =
;---  SMSC usb3503 HSIC HUB
usb_hsic_usb3503_flag       = 0
usb_hsic_hub_connect_gpio   =
usb_hsic_int_n_gpio         =
usb_hsic_reset_n_gpio       =