Browse Source

* android:
+ API to write to the Android system log.
+ API to redirect standard output and error to the Android system log. The redirection is performed automatically for shared libraries loaded by Java applications.

git-svn-id: trunk@34352 -

yury 9 years ago
parent
commit
419c6ce61e
4 changed files with 211 additions and 10 deletions
  1. 144 0
      rtl/android/sysandroid.inc
  2. 32 3
      rtl/android/sysandroidh.inc
  3. 3 0
      rtl/linux/system.pp
  4. 32 7
      rtl/unix/sysutils.pp

+ 144 - 0
rtl/android/sysandroid.inc

@@ -83,3 +83,147 @@ begin
     end;
     end;
   Result:=_ApiLevel;
   Result:=_ApiLevel;
 end;
 end;
+
+// ************* Android log
+
+var
+  DefaultLogTag: string[20];
+
+function __android_log_write(prio: longint; tag, text: pchar): longint; cdecl; external 'log' name '__android_log_write';
+
+procedure SysLogWrite(Priority: longint; Tag, Msg: PAnsiChar);
+begin
+  __android_log_write(Priority, Tag, Msg);
+end;
+
+procedure SysLogWrite(Priority: longint; Msg: PAnsiChar);
+begin
+  SysLogWrite(Priority, @DefaultLogTag[1], Msg);
+end;
+
+procedure SysLogWrite(Msg: PAnsiChar);
+begin
+  SysLogWrite(DefaultSysLogPriority, @DefaultLogTag[1], Msg);
+end;
+
+// ************* STDIO redirection to Android log
+
+const
+  IOBufferLength = 512;
+var
+  IOBuf : array[0..IOBufferLength] of char;
+  IOLen : SizeInt;
+  IORedirected: boolean;
+
+procedure OutputIOBuffer(Var F: TextRec);
+var
+  p: longint;
+begin
+  if (@F = @ErrOutput) or (@F = @StdErr) then
+    p:=ANDROID_LOG_ERROR
+  else
+    p:=DefaultSysLogPriority;
+  SysLogWrite(p, IOBuf);
+  IOLen:=0;
+end;
+
+procedure IOWrite(Var F: TextRec);
+var
+  i, len : SizeInt;
+Begin
+  while F.BufPos>0 do
+    begin
+      begin
+        if F.BufPos + IOLen > IOBufferLength then
+          len:=IOBufferLength - IOLen
+        else
+          len:=F.BufPos;
+        i:=0;
+        while i < len do
+          begin
+            if F.bufptr^[i] in [#10, #13] then
+              begin
+                IOBuf[IOLen]:=#0;
+                OutputIOBuffer(F);
+                Inc(i);
+                if (i < len) and (F.bufptr^[i - 1] = #13) and (F.bufptr^[i] = #10) then
+                  Inc(i);
+              end
+            else
+              begin
+                IOBuf[IOLen]:=F.bufptr^[i];
+                Inc(IOLen);
+                Inc(i);
+              end;
+          end;
+        IOBuf[IOLen]:=#0;
+      end;
+      if IOLen = IOBufferLength then
+        OutputIOBuffer(F);
+      Dec(F.BufPos, len);
+    end;
+End;
+
+procedure IOClose(Var F: TextRec);
+begin
+  if IOLen > 0 then
+    OutputIOBuffer(F);
+end;
+
+procedure IOOpen(Var F: TextRec);
+Begin
+  TextRec(F).InOutFunc:=@IOWrite;
+  TextRec(F).FlushFunc:=@IOWrite;
+  TextRec(F).CloseFunc:=@IOClose;
+  IOLen:=0;
+End;
+
+procedure RedirectFile(Var T: Text);
+begin
+  Assign(T,'');
+  TextRec(T).OpenFunc:=@IOOpen;
+  Rewrite(T);
+end;
+
+procedure RedirectOutputToSysLog;
+begin
+  if IORedirected then exit;
+  IORedirected:=True;
+  RedirectFile(Output);
+  RedirectFile(StdOut);
+  RedirectFile(ErrOutput);
+  RedirectFile(StdErr);
+end;
+
+procedure SetDefaultSysLogTag(const Tag: string);
+var
+  len: longint;
+begin
+  DefaultLogTag:=Tag;
+  len:=Length(DefaultLogTag);
+  if len = High(DefaultLogTag) then
+    Dec(len);
+  DefaultLogTag[len + 1]:=#0;
+end;
+
+procedure InitAndroid;
+var
+  i: integer;
+  s: string;
+begin
+  IsJniLibrary:=IsLibrary and (Pos('/system/', ParamStr(0)) = 1);
+  if IsJniLibrary then
+    begin
+      // The library is loaded by a Java app. The proper tag will be set by SysUtils.
+      SetDefaultSysLogTag('FPC');
+      RedirectOutputToSysLog;
+    end
+  else
+    begin
+      s:=ParamStr(0);
+      i:=Length(s);
+      while (i > 0) and (s[i] <> '/') do
+        Dec(i);
+      SetDefaultSysLogTag(Copy(s, i + 1, MaxInt));
+    end;
+end;

+ 32 - 3
rtl/android/sysandroidh.inc

@@ -3,7 +3,7 @@
     Copyright (c) 2016 by Yury Sidorov,
     Copyright (c) 2016 by Yury Sidorov,
     member of the Free Pascal development team.
     member of the Free Pascal development team.
 
 
-    Android-specific part of the System unit.
+    Header of Android-specific part of the System unit.
 
 
     See the file COPYING.FPC, included in this distribution,
     See the file COPYING.FPC, included in this distribution,
     for details about the copyright.
     for details about the copyright.
@@ -13,7 +13,36 @@
     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  **********************************************************************}
  **********************************************************************}
 
 
-// Returns an Android system property
+const
+  // Android system log priority
+  ANDROID_LOG_VERBOSE = 2;
+  ANDROID_LOG_DEBUG   = 3;
+  ANDROID_LOG_INFO    = 4;
+  ANDROID_LOG_WARN    = 5;
+  ANDROID_LOG_ERROR   = 6;
+  ANDROID_LOG_FATAL   = 7;
+
+// Default priority for syslog messages.
+var DefaultSysLogPriority: longint = ANDROID_LOG_DEBUG;
+
+// Set default tag for syslog messages. Initially the tag is set to the current module name.
+procedure SetDefaultSysLogTag(const Tag: string);
+
+// Write a message to the Android system log.
+procedure SysLogWrite(Priority: longint; Tag, Msg: PAnsiChar); overload;
+procedure SysLogWrite(Priority: longint; Msg: PAnsiChar); overload;
+procedure SysLogWrite(Msg: PAnsiChar); overload;
+
+// Redirects standard output and error to the Android system log.
+// The redirection is performed automatically for shared libraries loaded by Java applications.
+procedure RedirectOutputToSysLog;
+
+// Returns an Android system property.
 function GetSystemProperty(Name: PAnsiChar): shortstring;
 function GetSystemProperty(Name: PAnsiChar): shortstring;
-// Returns an Android API level of the current system
+
+// Returns an Android API level of the host system.
 function SystemApiLevel: shortint;
 function SystemApiLevel: shortint;
+
+// True when the current program is a shared library loaded by a Java application.
+var IsJniLibrary: boolean;
+

+ 3 - 0
rtl/linux/system.pp

@@ -378,6 +378,9 @@ begin
   InitSystemThreads;
   InitSystemThreads;
   { dynamic libraries }
   { dynamic libraries }
   InitSystemDynLibs;
   InitSystemDynLibs;
+{$ifdef android}
+  InitAndroid;
+{$endif android}
   { restore original signal handlers in case this is a library }
   { restore original signal handlers in case this is a library }
   if IsLibrary then
   if IsLibrary then
     RestoreOldSignalHandlers;
     RestoreOldSignalHandlers;

+ 32 - 7
rtl/unix/sysutils.pp

@@ -80,6 +80,9 @@ procedure UnhookSignal(RtlSigNum: Integer; OnlyIfHooked: Boolean = True);
 implementation
 implementation
 
 
 Uses
 Uses
+{$ifdef android}
+  dl,
+{$endif android}
   {$ifdef FPC_USE_LIBC}initc{$ELSE}Syscall{$ENDIF}, Baseunix, unixutil;
   {$ifdef FPC_USE_LIBC}initc{$ELSE}Syscall{$ENDIF}, Baseunix, unixutil;
 
 
 type
 type
@@ -1428,7 +1431,6 @@ end;
 
 
 var
 var
   _HomeDir: string;
   _HomeDir: string;
-  IsNDKLib: boolean;
 
 
 Function GetHomeDir : String;
 Function GetHomeDir : String;
 var
 var
@@ -1438,7 +1440,7 @@ begin
   Result:=_HomeDir;
   Result:=_HomeDir;
   if Result <> '' then
   if Result <> '' then
     exit;
     exit;
-  if IsLibrary then
+  if IsJniLibrary then
     begin
     begin
       // For shared library get the package name of a host Java application
       // For shared library get the package name of a host Java application
       h:=FileOpen('/proc/self/cmdline', fmOpenRead or fmShareDenyNone);
       h:=FileOpen('/proc/self/cmdline', fmOpenRead or fmShareDenyNone);
@@ -1449,8 +1451,8 @@ begin
           SetLength(Result, strlen(PChar(Result)));
           SetLength(Result, strlen(PChar(Result)));
           FileClose(h);
           FileClose(h);
           Result:='/data/data/' + Result;
           Result:='/data/data/' + Result;
-          IsNDKLib:=DirectoryExists(Result);
-          if IsNDKLib then
+          IsJniLibrary:=DirectoryExists(Result);
+          if IsJniLibrary then
             Result:=Result + '/files/'
             Result:=Result + '/files/'
           else
           else
             Result:='';  // No package
             Result:='';  // No package
@@ -1497,7 +1499,7 @@ begin
   else
   else
     Result:=IncludeTrailingPathDelimiter(XdgConfigHome);
     Result:=IncludeTrailingPathDelimiter(XdgConfigHome);
 {$ifdef android}
 {$ifdef android}
-  if IsNDKLib then
+  if IsJniLibrary then
     exit;
     exit;
 {$endif android}
 {$endif android}
   if VendorName<>'' then
   if VendorName<>'' then
@@ -1513,7 +1515,7 @@ begin
   else
   else
     Result:=IncludeTrailingPathDelimiter(XdgConfigHome);
     Result:=IncludeTrailingPathDelimiter(XdgConfigHome);
 {$ifdef android}
 {$ifdef android}
-  if IsNDKLib then
+  if IsJniLibrary then
     begin
     begin
       Result:=Result+'config'+ConfigExtension;
       Result:=Result+'config'+ConfigExtension;
       exit;
       exit;
@@ -1596,6 +1598,26 @@ begin
  Result := -Tzseconds div 60; 
  Result := -Tzseconds div 60; 
 end;
 end;
 
 
+{$ifdef android}
+
+procedure InitAndroid;
+var
+  dlinfo: dl_info;
+  s: string;
+begin
+  if IsJniLibrary then
+    begin
+      FillChar(dlinfo, sizeof(dlinfo), 0);
+      dladdr(@InitAndroid, @dlinfo);
+      s:=dlinfo.dli_fname;
+      if s <> '' then
+        SetDefaultSysLogTag(ExtractFileName(s));
+    end;
+end;
+
+{$endif android}
+
+
 {****************************************************************************
 {****************************************************************************
                               Initialization code
                               Initialization code
 ****************************************************************************}
 ****************************************************************************}
@@ -1605,7 +1627,10 @@ Initialization
   InitInternational;    { Initialize internationalization settings }
   InitInternational;    { Initialize internationalization settings }
   SysConfigDir:='/etc'; { Initialize system config dir }
   SysConfigDir:='/etc'; { Initialize system config dir }
   OnBeep:=@SysBeep;
   OnBeep:=@SysBeep;
-  
+{$ifdef android}
+  InitAndroid;
+{$endif android}
+
 Finalization
 Finalization
   FreeDriveStr;
   FreeDriveStr;
   DoneExceptions;
   DoneExceptions;